[CRT] Massively improve performance of rand_s
[reactos.git] / dll / cpl / input / input_list.c
index 0d9718d..e7026fe 100644 (file)
 * PROJECT:         input.dll
 * FILE:            dll/cpl/input/input_list.c
 * PURPOSE:         input.dll
-* PROGRAMMER:      Dmitry Chapyshev (dmitry@reactos.org)
+* PROGRAMMERS:     Dmitry Chapyshev (dmitry@reactos.org)
+*                  Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
 */
 
 #include "input_list.h"
+#define NOTHING
 
+typedef struct
+{
+    PWCHAR FontName;
+    PWCHAR SubFontName;
+} MUI_SUBFONT;
+
+#include "../../../base/setup/lib/muifonts.h"
+
+BOOL UpdateRegistryForFontSubstitutes(MUI_SUBFONT *pSubstitutes)
+{
+    DWORD cbData;
+    HKEY hKey;
+    static const WCHAR pszKey[] =
+        L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes";
+
+    if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, pszKey, 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
+        return FALSE;
+
+    /* Overwrite only */
+    for (; pSubstitutes->FontName; ++pSubstitutes)
+    {
+        cbData = (lstrlenW(pSubstitutes->SubFontName) + 1) * sizeof(WCHAR);
+        RegSetValueExW(hKey, pSubstitutes->FontName, 0,
+            REG_SZ, (LPBYTE)pSubstitutes->SubFontName, cbData);
+    }
+
+    RegCloseKey(hKey);
+    return TRUE;
+}
+
+VOID GetSystemLibraryPath(LPWSTR pszPath, INT cchPath, LPCWSTR pszFileName)
+{
+    WCHAR szSysDir[MAX_PATH];
+    GetSystemDirectoryW(szSysDir, ARRAYSIZE(szSysDir));
+    StringCchPrintfW(pszPath, cchPath, L"%s\\%s", szSysDir, pszFileName);
+}
+
+BOOL
+InputList_SetFontSubstitutes(LCID dwLocaleId)
+{
+    MUI_SUBFONT *pSubstitutes;
+    WORD wLangID, wPrimaryLangID, wSubLangID;
+
+    wLangID = LANGIDFROMLCID(dwLocaleId);
+    wPrimaryLangID = PRIMARYLANGID(wLangID);
+    wSubLangID = SUBLANGID(wLangID);
+
+    /* FIXME: Add more if necessary */
+    switch (wPrimaryLangID)
+    {
+        default:
+            pSubstitutes = LatinFonts;
+            break;
+        case LANG_AZERI:
+        case LANG_BELARUSIAN:
+        case LANG_BULGARIAN:
+        case LANG_KAZAK:
+        case LANG_RUSSIAN:
+        case LANG_SERBIAN:
+        case LANG_TATAR:
+        case LANG_UKRAINIAN:
+        case LANG_UZBEK:
+            pSubstitutes = CyrillicFonts;
+            break;
+        case LANG_GREEK:
+            pSubstitutes = GreekFonts;
+            break;
+        case LANG_HEBREW:
+            pSubstitutes = HebrewFonts;
+            break;
+        case LANG_CHINESE:
+            switch (wSubLangID)
+            {
+                case SUBLANG_CHINESE_SIMPLIFIED:
+                case SUBLANG_CHINESE_SINGAPORE:
+                    pSubstitutes = ChineseSimplifiedFonts;
+                    break;
+                case SUBLANG_CHINESE_TRADITIONAL:
+                case SUBLANG_CHINESE_HONGKONG:
+                case SUBLANG_CHINESE_MACAU:
+                    pSubstitutes = ChineseTraditionalFonts;
+                    break;
+                default:
+                    pSubstitutes = NULL;
+                    DebugBreak();
+                    break;
+            }
+            break;
+        case LANG_JAPANESE:
+            pSubstitutes = JapaneseFonts;
+            break;
+        case LANG_KOREAN:
+            pSubstitutes = KoreanFonts;
+            break;
+        case LANG_ARABIC:
+        case LANG_ARMENIAN:
+        case LANG_BENGALI:
+        case LANG_FARSI:
+        case LANG_GEORGIAN:
+        case LANG_GUJARATI:
+        case LANG_HINDI:
+        case LANG_KONKANI:
+        case LANG_MARATHI:
+        case LANG_PUNJABI:
+        case LANG_SANSKRIT:
+        case LANG_TAMIL:
+        case LANG_TELUGU:
+        case LANG_THAI:
+        case LANG_URDU:
+        case LANG_VIETNAMESE:
+            pSubstitutes = UnicodeFonts;
+            break;
+    }
+
+    if (pSubstitutes)
+    {
+        UpdateRegistryForFontSubstitutes(pSubstitutes);
+        return TRUE;
+    }
+    return FALSE;
+}
 
 static INPUT_LIST_NODE *_InputList = NULL;
 
@@ -17,29 +140,28 @@ InputList_AppendNode(VOID)
     INPUT_LIST_NODE *pCurrent;
     INPUT_LIST_NODE *pNew;
 
-    pCurrent = _InputList;
-
     pNew = (INPUT_LIST_NODE*)malloc(sizeof(INPUT_LIST_NODE));
     if (pNew == NULL)
         return NULL;
 
     ZeroMemory(pNew, sizeof(INPUT_LIST_NODE));
 
-    if (pCurrent == NULL)
+    if (_InputList == NULL) /* Empty? */
     {
         _InputList = pNew;
+        return pNew;
     }
-    else
-    {
-        while (pCurrent->pNext != NULL)
-        {
-            pCurrent = pCurrent->pNext;
-        }
 
-        pNew->pPrev = pCurrent;
-        pCurrent->pNext = pNew;
+    /* Find last node */
+    for (pCurrent = _InputList; pCurrent->pNext; pCurrent = pCurrent->pNext)
+    {
+        NOTHING;
     }
 
+    /* Add to the end */
+    pCurrent->pNext = pNew;
+    pNew->pPrev = pCurrent;
+
     return pNew;
 }
 
@@ -75,20 +197,17 @@ VOID
 InputList_Destroy(VOID)
 {
     INPUT_LIST_NODE *pCurrent;
+    INPUT_LIST_NODE *pNext;
 
     if (_InputList == NULL)
         return;
 
-    pCurrent = _InputList;
-
-    while (pCurrent != NULL)
+    for (pCurrent = _InputList; pCurrent; pCurrent = pNext)
     {
-        INPUT_LIST_NODE *pNext = pCurrent->pNext;
+        pNext = pCurrent->pNext;
 
         free(pCurrent->pszIndicator);
         free(pCurrent);
-
-        pCurrent = pNext;
     }
 
     _InputList = NULL;
@@ -96,11 +215,12 @@ InputList_Destroy(VOID)
 
 
 static BOOL
-InputList_PrepareUserRegistry(VOID)
+InputList_PrepareUserRegistry(PHKEY phPreloadKey, PHKEY phSubstKey)
 {
     BOOL bResult = FALSE;
-    HKEY hTempKey = NULL;
-    HKEY hKey = NULL;
+    HKEY hKey;
+
+    *phPreloadKey = *phSubstKey = NULL;
 
     if (RegOpenKeyExW(HKEY_CURRENT_USER,
                       L"Keyboard Layout",
@@ -114,101 +234,143 @@ InputList_PrepareUserRegistry(VOID)
         RegCloseKey(hKey);
     }
 
-    if (RegCreateKeyW(HKEY_CURRENT_USER, L"Keyboard Layout", &hKey) != ERROR_SUCCESS)
+    if (RegCreateKeyW(HKEY_CURRENT_USER, L"Keyboard Layout", &hKey) == ERROR_SUCCESS &&
+        RegCreateKeyW(hKey, L"Preload", phPreloadKey) == ERROR_SUCCESS &&
+        RegCreateKeyW(hKey, L"Substitutes", phSubstKey) == ERROR_SUCCESS)
     {
-        goto Cleanup;
+        bResult = TRUE;
     }
 
-    if (RegCreateKeyW(hKey, L"Preload", &hTempKey) != ERROR_SUCCESS)
-    {
-        goto Cleanup;
-    }
+    if (hKey)
+        RegCloseKey(hKey);
 
-    RegCloseKey(hTempKey);
+    return bResult;
+}
 
-    if (RegCreateKeyW(hKey, L"Substitutes", &hTempKey) != ERROR_SUCCESS)
+static BOOL
+InputList_FindPreloadKLID(HKEY hPreloadKey, DWORD dwKLID)
+{
+    DWORD dwNumber, dwType, cbValue;
+    WCHAR szNumber[16], szValue[KL_NAMELENGTH], szKLID[KL_NAMELENGTH];
+
+    StringCchPrintfW(szKLID, ARRAYSIZE(szKLID), L"%08x", dwKLID);
+
+    for (dwNumber = 1; dwNumber <= 1000; ++dwNumber)
     {
-        goto Cleanup;
-    }
+        StringCchPrintfW(szNumber, ARRAYSIZE(szNumber), L"%u", dwNumber);
 
-    RegCloseKey(hTempKey);
+        cbValue = ARRAYSIZE(szValue) * sizeof(WCHAR);
+        if (RegQueryValueExW(hPreloadKey, szNumber, NULL, &dwType,
+                             (LPBYTE)szValue, &cbValue) != ERROR_SUCCESS)
+        {
+            break;
+        }
 
-    bResult = TRUE;
+        if (dwType != REG_SZ)
+            continue;
 
-Cleanup:
-    if (hTempKey != NULL)
-        RegCloseKey(hTempKey);
-    if (hKey != NULL)
-        RegCloseKey(hKey);
+        szValue[ARRAYSIZE(szValue) - 1] = 0;
+        if (_wcsicmp(szKLID, szValue) == 0)
+            return TRUE;
+    }
 
-    return bResult;
+    return FALSE;
 }
 
-
-static VOID
-InputList_AddInputMethodToUserRegistry(DWORD dwIndex, INPUT_LIST_NODE *pNode)
+static BOOL
+InputList_WriteSubst(HKEY hSubstKey, DWORD dwPhysicalKLID, DWORD dwLogicalKLID)
 {
-    WCHAR szMethodIndex[MAX_PATH];
-    WCHAR szPreload[MAX_PATH];
-    BOOL bIsImeMethod = FALSE;
-    HKEY hKey;
+    DWORD cbValue;
+    WCHAR szLogicalKLID[KL_NAMELENGTH], szPhysicalKLID[KL_NAMELENGTH];
 
-    StringCchPrintfW(szMethodIndex, ARRAYSIZE(szMethodIndex), L"%lu", dwIndex);
+    StringCchPrintfW(szLogicalKLID, ARRAYSIZE(szLogicalKLID), L"%08x", dwLogicalKLID);
+    StringCchPrintfW(szPhysicalKLID, ARRAYSIZE(szPhysicalKLID), L"%08x", dwPhysicalKLID);
 
-    /* Check is IME method */
-    if ((HIWORD(pNode->pLayout->dwId) & 0xF000) == 0xE000)
-    {
-        StringCchPrintfW(szPreload, ARRAYSIZE(szPreload), L"%08X", pNode->pLayout->dwId);
-        bIsImeMethod = TRUE;
-    }
-    else
-    {
-        StringCchPrintfW(szPreload, ARRAYSIZE(szPreload), L"%08X", pNode->pLocale->dwId);
-    }
+    cbValue = (wcslen(szPhysicalKLID) + 1) * sizeof(WCHAR);
+    return RegSetValueExW(hSubstKey, szLogicalKLID, 0, REG_SZ, (LPBYTE)szPhysicalKLID,
+                          cbValue) == ERROR_SUCCESS;
+}
 
-    if (RegOpenKeyExW(HKEY_CURRENT_USER,
-                      L"Keyboard Layout\\Preload",
-                      0,
-                      KEY_SET_VALUE,
-                      &hKey) == ERROR_SUCCESS)
+static DWORD
+InputList_DoSubst(HKEY hPreloadKey, HKEY hSubstKey,
+                  DWORD dwPhysicalKLID, DWORD dwLogicalKLID)
+{
+    DWORD iTrial;
+    BOOL bSubstNeeded = (dwPhysicalKLID != dwLogicalKLID) || (HIWORD(dwPhysicalKLID) != 0);
+
+    for (iTrial = 1; iTrial <= 1000; ++iTrial)
     {
-        RegSetValueExW(hKey,
-                       szMethodIndex,
-                       0,
-                       REG_SZ,
-                       (LPBYTE)szPreload,
-                       (wcslen(szPreload) + 1) * sizeof(WCHAR));
+        if (!InputList_FindPreloadKLID(hPreloadKey, dwLogicalKLID)) /* Not found? */
+        {
+            if (bSubstNeeded)
+            {
+                /* Write now */
+                InputList_WriteSubst(hSubstKey, dwPhysicalKLID, dwLogicalKLID);
+            }
+            return dwLogicalKLID;
+        }
 
-        RegCloseKey(hKey);
-    }
+        bSubstNeeded = TRUE;
 
-    if (pNode->pLocale->dwId != pNode->pLayout->dwId && bIsImeMethod == FALSE)
-    {
-        if (RegOpenKeyExW(HKEY_CURRENT_USER,
-                          L"Keyboard Layout\\Substitutes",
-                          0,
-                          KEY_SET_VALUE,
-                          &hKey) == ERROR_SUCCESS)
+        /* Calculate the next logical KLID */
+        if (!IS_SUBST_KLID(dwLogicalKLID))
+        {
+            dwLogicalKLID |= SUBST_MASK;
+        }
+        else
         {
-            WCHAR szSubstitutes[MAX_PATH];
+            WORD wLow = LOWORD(dwLogicalKLID);
+            WORD wHigh = HIWORD(dwLogicalKLID);
+            dwLogicalKLID = MAKELONG(wLow, wHigh + 1);
+        }
+    }
 
-            StringCchPrintfW(szSubstitutes, ARRAYSIZE(szSubstitutes), L"%08X", pNode->pLayout->dwId);
+    return 0;
+}
 
-            RegSetValueExW(hKey,
-                           szPreload,
-                           0,
-                           REG_SZ,
-                           (LPBYTE)szSubstitutes,
-                           (wcslen(szSubstitutes) + 1) * sizeof(WCHAR));
+static VOID
+InputList_AddInputMethodToUserRegistry(
+    HKEY hPreloadKey,
+    HKEY hSubstKey,
+    DWORD dwNumber,
+    INPUT_LIST_NODE *pNode)
+{
+    WCHAR szNumber[32], szLogicalKLID[KL_NAMELENGTH];
+    DWORD dwPhysicalKLID, dwLogicalKLID, cbValue;
+    HKL hKL = pNode->hkl;
 
-            RegCloseKey(hKey);
-        }
+    if (IS_IME_HKL(hKL)) /* IME? */
+    {
+        /* Do not substitute the IME KLID */
+        dwLogicalKLID = dwPhysicalKLID = HandleToUlong(hKL);
+    }
+    else
+    {
+        /* Substitute the KLID if necessary */
+        dwPhysicalKLID = pNode->pLayout->dwKLID;
+        dwLogicalKLID = pNode->pLocale->dwId;
+        dwLogicalKLID = InputList_DoSubst(hPreloadKey, hSubstKey, dwPhysicalKLID, dwLogicalKLID);
     }
 
+    /* Write the Preload value (number |--> logical KLID) */
+    StringCchPrintfW(szNumber, ARRAYSIZE(szNumber), L"%lu", dwNumber);
+    StringCchPrintfW(szLogicalKLID, ARRAYSIZE(szLogicalKLID), L"%08x", dwLogicalKLID);
+    cbValue = (wcslen(szLogicalKLID) + 1) * sizeof(WCHAR);
+    RegSetValueExW(hPreloadKey,
+                   szNumber,
+                   0,
+                   REG_SZ,
+                   (LPBYTE)szLogicalKLID,
+                   cbValue);
+
     if ((pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED) ||
         (pNode->wFlags & INPUT_LIST_NODE_FLAG_EDITED))
     {
-        pNode->hkl = LoadKeyboardLayoutW(szPreload, KLF_SUBSTITUTE_OK | KLF_NOTELLSHELL);
+        UINT uFlags = KLF_SUBSTITUTE_OK | KLF_NOTELLSHELL;
+        if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
+            uFlags |= KLF_REPLACELANG;
+
+        pNode->hkl = LoadKeyboardLayoutW(szLogicalKLID, uFlags);
     }
 }
 
@@ -216,22 +378,51 @@ InputList_AddInputMethodToUserRegistry(DWORD dwIndex, INPUT_LIST_NODE *pNode)
 /*
  * Writes any changes in input methods to the registry
  */
-VOID
+BOOL
 InputList_Process(VOID)
 {
     INPUT_LIST_NODE *pCurrent;
-    DWORD dwIndex;
+    DWORD dwNumber;
+    BOOL bRet = FALSE;
+    HKEY hPreloadKey, hSubstKey;
+    HKL hDefaultKL = NULL;
+
+    if (!InputList_PrepareUserRegistry(&hPreloadKey, &hSubstKey))
+    {
+        if (hPreloadKey)
+            RegCloseKey(hPreloadKey);
+        if (hSubstKey)
+            RegCloseKey(hSubstKey);
+        return FALSE;
+    }
 
-    /* Process deleted and edited input methods */
+    /* Find change in the IME HKLs */
+    for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
+    {
+        if (!IS_IME_HKL(pCurrent->hkl))
+            continue;
+
+        if ((pCurrent->wFlags & INPUT_LIST_NODE_FLAG_ADDED) ||
+            (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED) ||
+            (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED))
+        {
+            bRet = TRUE; /* Reboot is needed */
+            break;
+        }
+    }
+
+    /* Process DELETED and EDITED entries */
     for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
     {
         if ((pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) ||
             (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED))
         {
+
+            /* Only unload the DELETED and EDITED entries */
             if (UnloadKeyboardLayout(pCurrent->hkl))
             {
-                /* Only unload the edited input method, but does not delete it from the list */
-                if (!(pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED))
+                /* But the EDITED entries are used later */
+                if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
                 {
                     InputList_RemoveNode(pCurrent);
                 }
@@ -239,46 +430,71 @@ InputList_Process(VOID)
         }
     }
 
-    InputList_PrepareUserRegistry();
-
-    /* Find default input method */
+    /* Add the DEFAULT entry and set font substitutes */
     for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
     {
+        if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
+            continue;
+
         if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
         {
-            InputList_AddInputMethodToUserRegistry(1, pCurrent);
+            bRet = InputList_SetFontSubstitutes(pCurrent->pLocale->dwId);
+            InputList_AddInputMethodToUserRegistry(hPreloadKey, hSubstKey, 1, pCurrent);
+
+            /* Activate the DEFAULT entry */
+            ActivateKeyboardLayout(pCurrent->hkl, KLF_RESET);
+
+            /* Save it */
+            hDefaultKL = pCurrent->hkl;
             break;
         }
     }
 
-    if (SystemParametersInfoW(SPI_SETDEFAULTINPUTLANG,
-                              0,
-                              (LPVOID)((LPDWORD)&pCurrent->hkl),
-                              0))
+    /* Add entries except DEFAULT to registry */
+    dwNumber = 2;
+    for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
     {
-        DWORD dwRecipients;
+        if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
+            continue;
+        if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
+            continue;
+
+        InputList_AddInputMethodToUserRegistry(hPreloadKey, hSubstKey, dwNumber, pCurrent);
 
-        dwRecipients = BSM_ALLCOMPONENTS;
+        ++dwNumber;
+    }
+
+    /* Remove ADDED and EDITED flags */
+    for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
+    {
+        pCurrent->wFlags &= ~(INPUT_LIST_NODE_FLAG_ADDED | INPUT_LIST_NODE_FLAG_EDITED);
+    }
+
+    /* Change the default keyboard language */
+    if (SystemParametersInfoW(SPI_SETDEFAULTINPUTLANG, 0, &hDefaultKL, 0))
+    {
+        DWORD dwRecipients = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
 
         BroadcastSystemMessageW(BSF_POSTMESSAGE,
                                 &dwRecipients,
                                 WM_INPUTLANGCHANGEREQUEST,
-                                0,
-                                (LPARAM)pCurrent->hkl);
+                                INPUTLANGCHANGE_SYSCHARSET,
+                                (LPARAM)hDefaultKL);
     }
 
-    /* Add methods to registry */
-    dwIndex = 2;
-
+    /* Retry to delete (in case of failure to delete the default keyboard) */
     for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
     {
-        if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
-            continue;
-
-        InputList_AddInputMethodToUserRegistry(dwIndex, pCurrent);
-
-        dwIndex++;
+        if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
+        {
+            UnloadKeyboardLayout(pCurrent->hkl);
+            InputList_RemoveNode(pCurrent);
+        }
     }
+
+    RegCloseKey(hPreloadKey);
+    RegCloseKey(hSubstKey);
+    return bRet;
 }
 
 
@@ -286,7 +502,7 @@ BOOL
 InputList_Add(LOCALE_LIST_NODE *pLocale, LAYOUT_LIST_NODE *pLayout)
 {
     WCHAR szIndicator[MAX_STR_LEN];
-    INPUT_LIST_NODE *pInput;
+    INPUT_LIST_NODE *pInput = NULL;
 
     if (pLocale == NULL || pLayout == NULL)
     {
@@ -295,16 +511,17 @@ InputList_Add(LOCALE_LIST_NODE *pLocale, LAYOUT_LIST_NODE *pLayout)
 
     for (pInput = _InputList; pInput != NULL; pInput = pInput->pNext)
     {
+        if (pInput->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
+            continue;
+
         if (pInput->pLocale == pLocale && pInput->pLayout == pLayout)
         {
-            return FALSE;
+            return FALSE; /* Already exists */
         }
     }
 
     pInput = InputList_AppendNode();
-
     pInput->wFlags = INPUT_LIST_NODE_FLAG_ADDED;
-
     pInput->pLocale = pLocale;
     pInput->pLayout = pLayout;
 
@@ -347,18 +564,42 @@ InputList_SetDefault(INPUT_LIST_NODE *pNode)
     }
 }
 
+INPUT_LIST_NODE *
+InputList_FindNextDefault(INPUT_LIST_NODE *pNode)
+{
+    INPUT_LIST_NODE *pCurrent;
+
+    for (pCurrent = pNode->pNext; pCurrent; pCurrent = pCurrent->pNext)
+    {
+        if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
+            continue;
+
+        return pCurrent;
+    }
+
+    for (pCurrent = pNode->pPrev; pCurrent; pCurrent = pCurrent->pPrev)
+    {
+        if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
+            continue;
+
+        return pCurrent;
+    }
+
+    return NULL;
+}
 
 /*
  * It marks the input method for deletion, but does not delete it directly.
  * To apply the changes using InputList_Process()
  */
-VOID
+BOOL
 InputList_Remove(INPUT_LIST_NODE *pNode)
 {
+    BOOL ret = FALSE;
     BOOL bRemoveNode = FALSE;
 
     if (pNode == NULL)
-        return;
+        return FALSE;
 
     if (pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED)
     {
@@ -370,94 +611,190 @@ InputList_Remove(INPUT_LIST_NODE *pNode)
     }
     else
     {
-        pNode->wFlags = INPUT_LIST_NODE_FLAG_DELETED;
+        pNode->wFlags |= INPUT_LIST_NODE_FLAG_DELETED;
     }
 
     if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
     {
-        if (pNode->pNext != NULL)
-        {
-            pNode->pNext->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
-        }
-        else if (pNode->pPrev != NULL)
-        {
-            pNode->pPrev->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
-        }
+        INPUT_LIST_NODE *pCurrent = InputList_FindNextDefault(pNode);
+        if (pCurrent)
+            pCurrent->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
+
+        pNode->wFlags &= ~INPUT_LIST_NODE_FLAG_DEFAULT;
+        ret = TRUE; /* default input is changed */
     }
 
-    if (bRemoveNode != FALSE)
+    if (bRemoveNode)
     {
         InputList_RemoveNode(pNode);
     }
+
+    return ret;
 }
 
+BOOL
+InputList_RemoveByLang(LANGID wLangId)
+{
+    BOOL ret = FALSE;
+    INPUT_LIST_NODE *pCurrent;
+
+Retry:
+    for (pCurrent = _InputList; pCurrent; pCurrent = pCurrent->pNext)
+    {
+        if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
+            continue;
+
+        if (LOWORD(pCurrent->pLocale->dwId) == wLangId)
+        {
+            if (InputList_Remove(pCurrent))
+                ret = TRUE; /* default input is changed */
+            goto Retry;
+        }
+    }
+
+    return ret;
+}
 
 VOID
 InputList_Create(VOID)
 {
-    INT iLayoutCount;
-    HKL *pLayoutList;
+    INT iLayoutCount, iIndex;
+    WCHAR szIndicator[MAX_STR_LEN];
+    INPUT_LIST_NODE *pInput;
+    HKL *pLayoutList, hklDefault;
+
+    SystemParametersInfoW(SPI_GETDEFAULTINPUTLANG, 0, &hklDefault, 0);
 
     iLayoutCount = GetKeyboardLayoutList(0, NULL);
     pLayoutList = (HKL*) malloc(iLayoutCount * sizeof(HKL));
 
-    if (pLayoutList != NULL)
+    if (!pLayoutList || GetKeyboardLayoutList(iLayoutCount, pLayoutList) <= 0)
     {
-        if (GetKeyboardLayoutList(iLayoutCount, pLayoutList) > 0)
+        free(pLayoutList);
+        return;
+    }
+
+    for (iIndex = 0; iIndex < iLayoutCount; ++iIndex)
+    {
+        HKL hKL = pLayoutList[iIndex];
+        LOCALE_LIST_NODE *pLocale = LocaleList_GetByHkl(hKL);
+        LAYOUT_LIST_NODE *pLayout = LayoutList_GetByHkl(hKL);
+        if (!pLocale || !pLayout)
+            continue;
+
+        pInput = InputList_AppendNode();
+        pInput->pLocale = pLocale;
+        pInput->pLayout = pLayout;
+        pInput->hkl     = hKL;
+
+        if (pInput->hkl == hklDefault) /* Default HKL? */
         {
-            INT iIndex;
+            pInput->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
+            hklDefault = NULL; /* No more default item */
+        }
 
-            for (iIndex = 0; iIndex < iLayoutCount; iIndex++)
+        /* Get abbrev language name */
+        szIndicator[0] = 0;
+        if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId),
+                           LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE,
+                           szIndicator,
+                           ARRAYSIZE(szIndicator)))
+        {
+            size_t len = wcslen(szIndicator);
+            if (len > 0)
             {
-                LOCALE_LIST_NODE *pLocale = LocaleList_GetByHkl(pLayoutList[iIndex]);
-                LAYOUT_LIST_NODE *pLayout = LayoutList_GetByHkl(pLayoutList[iIndex]);
+                szIndicator[len - 1] = 0;
+                pInput->pszIndicator = _wcsdup(szIndicator);
+            }
+        }
+    }
 
-                if (pLocale != NULL && pLayout != NULL)
-                {
-                    WCHAR szIndicator[MAX_STR_LEN] = { 0 };
-                    INPUT_LIST_NODE *pInput;
-                    HKL hklDefault;
-
-                    pInput = InputList_AppendNode();
-
-                    pInput->pLocale = pLocale;
-                    pInput->pLayout = pLayout;
-                    pInput->hkl     = pLayoutList[iIndex];
-
-                    if (SystemParametersInfoW(SPI_GETDEFAULTINPUTLANG,
-                                              0,
-                                              (LPVOID)((LPDWORD)&hklDefault),
-                                              0) == FALSE)
-                    {
-                        hklDefault = GetKeyboardLayout(0);
-                    }
-
-                    if (pInput->hkl == hklDefault)
-                    {
-                        pInput->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
-                    }
-
-                    if (GetLocaleInfoW(LOWORD(pInput->pLocale->dwId),
-                                       LOCALE_SABBREVLANGNAME | LOCALE_NOUSEROVERRIDE,
-                                       szIndicator,
-                                       ARRAYSIZE(szIndicator)))
-                    {
-                        size_t len = wcslen(szIndicator);
-
-                        if (len > 0)
-                        {
-                            szIndicator[len - 1] = 0;
-                            pInput->pszIndicator = _wcsdup(szIndicator);
-                        }
-                    }
-                }
+    free(pLayoutList);
+}
+
+static INT InputList_Compare(INPUT_LIST_NODE *pNode1, INPUT_LIST_NODE *pNode2)
+{
+    INT nCompare = _wcsicmp(pNode1->pszIndicator, pNode2->pszIndicator);
+    if (nCompare != 0)
+        return nCompare;
+
+    return _wcsicmp(pNode1->pLayout->pszName, pNode2->pLayout->pszName);
+}
+
+VOID InputList_Sort(VOID)
+{
+    INPUT_LIST_NODE *pList = _InputList;
+    INPUT_LIST_NODE *pNext, *pPrev;
+    INPUT_LIST_NODE *pMinimum, *pNode;
+
+    _InputList = NULL;
+
+    while (pList)
+    {
+        /* Find the minimum node */
+        pMinimum = NULL;
+        for (pNode = pList; pNode; pNode = pNext)
+        {
+            pNext = pNode->pNext;
+
+            if (pMinimum == NULL)
+            {
+                pMinimum = pNode;
+            }
+            else if (InputList_Compare(pNode, pMinimum) < 0)
+            {
+                pMinimum = pNode;
             }
         }
 
-        free(pLayoutList);
+        // Remove pMinimum from pList
+        pNext = pMinimum->pNext;
+        pPrev = pMinimum->pPrev;
+        if (pNext)
+            pNext->pPrev = pPrev;
+        if (pPrev)
+            pPrev->pNext = pNext;
+        else
+            pList = pNext;
+
+        // Append pMinimum to _InputList
+        if (!_InputList)
+        {
+            pMinimum->pPrev = pMinimum->pNext = NULL;
+            _InputList = pMinimum;
+        }
+        else
+        {
+            /* Find last node */
+            for (pNode = _InputList; pNode->pNext; pNode = pNode->pNext)
+            {
+                NOTHING;
+            }
+
+            /* Add to the end */
+            pNode->pNext = pMinimum;
+            pMinimum->pPrev = pNode;
+            pMinimum->pNext = NULL;
+        }
     }
 }
 
+INT
+InputList_GetAliveCount(VOID)
+{
+    INPUT_LIST_NODE *pNode;
+    INT nCount = 0;
+
+    for (pNode = _InputList; pNode; pNode = pNode->pNext)
+    {
+        if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
+            continue;
+
+        ++nCount;
+    }
+
+    return nCount;
+}
 
 INPUT_LIST_NODE*
 InputList_GetFirst(VOID)