2016-08-08 20:30:36 +00:00
|
|
|
/*
|
|
|
|
* PROJECT: input.dll
|
|
|
|
* FILE: dll/cpl/input/input_list.c
|
|
|
|
* PURPOSE: input.dll
|
2017-11-13 01:36:52 +00:00
|
|
|
* PROGRAMMERS: Dmitry Chapyshev (dmitry@reactos.org)
|
|
|
|
* Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
|
2016-08-08 20:30:36 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "input_list.h"
|
2022-09-28 22:31:43 +00:00
|
|
|
#define NOTHING
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2017-11-13 01:36:52 +00:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
PWCHAR FontName;
|
|
|
|
PWCHAR SubFontName;
|
|
|
|
} MUI_SUBFONT;
|
|
|
|
|
[SETUPLIB][USETUP][INPUT.CPL] MUI integration with setuplib.
[SETUPLIB] Copy the mui* files from usetup into the setuplib for later usage, and add mui.c to build.
svn path=/branches/setup_improvements/; revision=75711
[SETUPLIB][USETUP] Start massaging the MUI files and split up what concerns only the usetup "resources",
and what concerns general language parameters (in the setuplib).
It may be interesting to retrieve the language parameters
from INF file (e.g. intl.inf) (suggested by Giannis).
svn path=/branches/setup_improvements/; revision=75715
[SETUPLIB] Add the necessary headers to mui.c to be able to compile.
[USETUP] Comment out the languages that don't have any resources.
svn path=/branches/setup_improvements/; revision=75716
[SETUPLIB][USETUP] Adjust MUI and settings code.
- Re-enable settings code concerning language, keyboards & layout
in the setuplib, and remove the old code in usetup.
- Remove useless code in setuplib's mui.c.
- Rename usetup's MUI "LanguageList" into "ResourceList" so as to
avoid colliding with the "LanguageList" of setuplib.
- Add the magic CMakeLists line "add_definitions(${I18N_DEFS})" that
I forgot previously, that caused the "LanguageList" of setuplib to be empty!
The code compiles and works during my tests.
svn path=/branches/setup_improvements/; revision=75717
[SETUPLIB] Change some string pointer types & function prototypes so that the usetup-specific
"SelectedLanguageId" variable is not used in the library.
svn path=/branches/setup_improvements/; revision=75719
[SETUPLIB] Change some function prototypes so that the usetup-specific "SelectedLanguageId" variable is not used in the library.
Also, make AddEntriesFromInfSection(), DefaultProcessEntry() and the typedef PPROCESS_ENTRY_ROUTINE private again.
svn path=/branches/setup_improvements/; revision=75720
svn path=/branches/setup_improvements/; revision=75724
[USETUP] Code adaptations.
- Transform some do{}while() loops into while(){} ones, since the lists on which we work may only contain one single {NULL} element.
- Modify MUIGetOEMCodePage() call after r75719.
- Use PCWSTR where needed.
svn path=/branches/setup_improvements/; revision=75722
[USETUP] Adjust some MUI & settings calls, following the commits r75719, r75720 and r75721.
svn path=/branches/setup_improvements/; revision=75723
[INPUT.CPL] Adjust inclusion of "muifonts.h" (commit 3dbd44f) due to changes introduced in r75711 and r75715.
2017-08-30 11:28:52 +00:00
|
|
|
#include "../../../base/setup/lib/muifonts.h"
|
2017-11-13 01:36:52 +00:00
|
|
|
|
|
|
|
BOOL UpdateRegistryForFontSubstitutes(MUI_SUBFONT *pSubstitutes)
|
|
|
|
{
|
|
|
|
DWORD cbData;
|
|
|
|
HKEY hKey;
|
|
|
|
static const WCHAR pszKey[] =
|
|
|
|
L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes";
|
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, pszKey, 0, KEY_ALL_ACCESS, &hKey) != ERROR_SUCCESS)
|
2017-11-13 01:36:52 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
VOID GetSystemLibraryPath(LPWSTR pszPath, INT cchPath, LPCWSTR pszFileName)
|
|
|
|
{
|
|
|
|
WCHAR szSysDir[MAX_PATH];
|
|
|
|
GetSystemDirectoryW(szSysDir, ARRAYSIZE(szSysDir));
|
|
|
|
StringCchPrintfW(pszPath, cchPath, L"%s\\%s", szSysDir, pszFileName);
|
|
|
|
}
|
|
|
|
|
2017-11-13 01:36:52 +00:00
|
|
|
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:
|
2021-10-11 13:24:21 +00:00
|
|
|
case SUBLANG_CHINESE_MACAU:
|
2017-11-13 01:36:52 +00:00
|
|
|
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;
|
|
|
|
}
|
2016-08-08 20:30:36 +00:00
|
|
|
|
|
|
|
static INPUT_LIST_NODE *_InputList = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
static INPUT_LIST_NODE*
|
|
|
|
InputList_AppendNode(VOID)
|
|
|
|
{
|
|
|
|
INPUT_LIST_NODE *pCurrent;
|
|
|
|
INPUT_LIST_NODE *pNew;
|
|
|
|
|
|
|
|
pNew = (INPUT_LIST_NODE*)malloc(sizeof(INPUT_LIST_NODE));
|
|
|
|
if (pNew == NULL)
|
|
|
|
return NULL;
|
|
|
|
|
2016-08-10 11:42:21 +00:00
|
|
|
ZeroMemory(pNew, sizeof(INPUT_LIST_NODE));
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
if (_InputList == NULL) /* Empty? */
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
|
|
|
_InputList = pNew;
|
2022-09-15 23:09:37 +00:00
|
|
|
return pNew;
|
2016-08-08 20:30:36 +00:00
|
|
|
}
|
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
/* Find last node */
|
|
|
|
for (pCurrent = _InputList; pCurrent->pNext; pCurrent = pCurrent->pNext)
|
|
|
|
{
|
2022-09-28 22:31:43 +00:00
|
|
|
NOTHING;
|
2016-08-08 20:30:36 +00:00
|
|
|
}
|
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
/* Add to the end */
|
|
|
|
pCurrent->pNext = pNew;
|
|
|
|
pNew->pPrev = pCurrent;
|
|
|
|
|
2016-08-08 20:30:36 +00:00
|
|
|
return pNew;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-09 14:01:52 +00:00
|
|
|
static VOID
|
|
|
|
InputList_RemoveNode(INPUT_LIST_NODE *pNode)
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
|
|
|
INPUT_LIST_NODE *pCurrent = pNode;
|
|
|
|
|
|
|
|
if (_InputList == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (pCurrent != NULL)
|
|
|
|
{
|
|
|
|
INPUT_LIST_NODE *pNext = pCurrent->pNext;
|
|
|
|
INPUT_LIST_NODE *pPrev = pCurrent->pPrev;
|
|
|
|
|
2016-08-09 14:01:52 +00:00
|
|
|
free(pCurrent->pszIndicator);
|
2016-08-08 20:30:36 +00:00
|
|
|
free(pCurrent);
|
|
|
|
|
|
|
|
if (pNext != NULL)
|
|
|
|
pNext->pPrev = pPrev;
|
|
|
|
|
|
|
|
if (pPrev != NULL)
|
|
|
|
pPrev->pNext = pNext;
|
|
|
|
else
|
|
|
|
_InputList = pNext;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
InputList_Destroy(VOID)
|
|
|
|
{
|
|
|
|
INPUT_LIST_NODE *pCurrent;
|
2022-09-15 23:09:37 +00:00
|
|
|
INPUT_LIST_NODE *pNext;
|
2016-08-08 20:30:36 +00:00
|
|
|
|
|
|
|
if (_InputList == NULL)
|
|
|
|
return;
|
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
for (pCurrent = _InputList; pCurrent; pCurrent = pNext)
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
pNext = pCurrent->pNext;
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2016-08-09 14:01:52 +00:00
|
|
|
free(pCurrent->pszIndicator);
|
2016-08-08 20:30:36 +00:00
|
|
|
free(pCurrent);
|
|
|
|
}
|
|
|
|
|
|
|
|
_InputList = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static BOOL
|
2022-09-15 23:09:37 +00:00
|
|
|
InputList_PrepareUserRegistry(PHKEY phPreloadKey, PHKEY phSubstKey)
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
|
|
|
BOOL bResult = FALSE;
|
2022-09-15 23:09:37 +00:00
|
|
|
HKEY hKey;
|
|
|
|
|
|
|
|
*phPreloadKey = *phSubstKey = NULL;
|
2016-08-08 20:30:36 +00:00
|
|
|
|
|
|
|
if (RegOpenKeyExW(HKEY_CURRENT_USER,
|
|
|
|
L"Keyboard Layout",
|
|
|
|
0,
|
|
|
|
KEY_ALL_ACCESS,
|
|
|
|
&hKey) == ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
RegDeleteKeyW(hKey, L"Preload");
|
|
|
|
RegDeleteKeyW(hKey, L"Substitutes");
|
|
|
|
|
|
|
|
RegCloseKey(hKey);
|
|
|
|
}
|
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
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)
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
bResult = TRUE;
|
2016-08-08 20:30:36 +00:00
|
|
|
}
|
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
if (hKey)
|
|
|
|
RegCloseKey(hKey);
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
return bResult;
|
|
|
|
}
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
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)
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
StringCchPrintfW(szNumber, ARRAYSIZE(szNumber), L"%u", dwNumber);
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
cbValue = ARRAYSIZE(szValue) * sizeof(WCHAR);
|
|
|
|
if (RegQueryValueExW(hPreloadKey, szNumber, NULL, &dwType,
|
|
|
|
(LPBYTE)szValue, &cbValue) != ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
if (dwType != REG_SZ)
|
|
|
|
continue;
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
szValue[ARRAYSIZE(szValue) - 1] = 0;
|
|
|
|
if (_wcsicmp(szKLID, szValue) == 0)
|
|
|
|
return TRUE;
|
|
|
|
}
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
return FALSE;
|
2016-08-08 20:30:36 +00:00
|
|
|
}
|
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
static BOOL
|
|
|
|
InputList_WriteSubst(HKEY hSubstKey, DWORD dwPhysicalKLID, DWORD dwLogicalKLID)
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
DWORD cbValue;
|
|
|
|
WCHAR szLogicalKLID[KL_NAMELENGTH], szPhysicalKLID[KL_NAMELENGTH];
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
StringCchPrintfW(szLogicalKLID, ARRAYSIZE(szLogicalKLID), L"%08x", dwLogicalKLID);
|
|
|
|
StringCchPrintfW(szPhysicalKLID, ARRAYSIZE(szPhysicalKLID), L"%08x", dwPhysicalKLID);
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
cbValue = (wcslen(szPhysicalKLID) + 1) * sizeof(WCHAR);
|
|
|
|
return RegSetValueExW(hSubstKey, szLogicalKLID, 0, REG_SZ, (LPBYTE)szPhysicalKLID,
|
|
|
|
cbValue) == ERROR_SUCCESS;
|
|
|
|
}
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
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)
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
if (!InputList_FindPreloadKLID(hPreloadKey, dwLogicalKLID)) /* Not found? */
|
|
|
|
{
|
|
|
|
if (bSubstNeeded)
|
|
|
|
{
|
|
|
|
/* Write now */
|
|
|
|
InputList_WriteSubst(hSubstKey, dwPhysicalKLID, dwLogicalKLID);
|
|
|
|
}
|
|
|
|
return dwLogicalKLID;
|
|
|
|
}
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
bSubstNeeded = TRUE;
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
/* Calculate the next logical KLID */
|
|
|
|
if (!IS_SUBST_KLID(dwLogicalKLID))
|
|
|
|
{
|
|
|
|
dwLogicalKLID |= SUBST_MASK;
|
|
|
|
}
|
|
|
|
else
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
WORD wLow = LOWORD(dwLogicalKLID);
|
|
|
|
WORD wHigh = HIWORD(dwLogicalKLID);
|
|
|
|
dwLogicalKLID = MAKELONG(wLow, wHigh + 1);
|
|
|
|
}
|
|
|
|
}
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
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;
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
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);
|
2016-08-08 20:30:36 +00:00
|
|
|
}
|
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
/* 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);
|
|
|
|
|
2016-08-09 18:26:43 +00:00
|
|
|
if ((pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED) ||
|
|
|
|
(pNode->wFlags & INPUT_LIST_NODE_FLAG_EDITED))
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
UINT uFlags = KLF_SUBSTITUTE_OK | KLF_NOTELLSHELL;
|
|
|
|
if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
|
|
|
|
uFlags |= KLF_REPLACELANG;
|
|
|
|
|
|
|
|
pNode->hkl = LoadKeyboardLayoutW(szLogicalKLID, uFlags);
|
2016-08-08 20:30:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-09 18:26:43 +00:00
|
|
|
/*
|
|
|
|
* Writes any changes in input methods to the registry
|
|
|
|
*/
|
2017-11-13 01:36:52 +00:00
|
|
|
BOOL
|
2016-08-08 20:30:36 +00:00
|
|
|
InputList_Process(VOID)
|
|
|
|
{
|
|
|
|
INPUT_LIST_NODE *pCurrent;
|
2022-09-15 23:09:37 +00:00
|
|
|
DWORD dwNumber;
|
2017-11-13 01:36:52 +00:00
|
|
|
BOOL bRet = FALSE;
|
2022-09-15 23:09:37 +00:00
|
|
|
HKEY hPreloadKey, hSubstKey;
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
if (!InputList_PrepareUserRegistry(&hPreloadKey, &hSubstKey))
|
|
|
|
{
|
|
|
|
if (hPreloadKey)
|
|
|
|
RegCloseKey(hPreloadKey);
|
|
|
|
if (hSubstKey)
|
|
|
|
RegCloseKey(hSubstKey);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process DELETED and EDITED entries */
|
2016-08-08 20:30:36 +00:00
|
|
|
for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
|
|
|
|
{
|
2016-08-09 18:26:43 +00:00
|
|
|
if ((pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED) ||
|
|
|
|
(pCurrent->wFlags & INPUT_LIST_NODE_FLAG_EDITED))
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
/* Only unload the DELETED and EDITED entries */
|
2016-08-09 18:26:43 +00:00
|
|
|
if (UnloadKeyboardLayout(pCurrent->hkl))
|
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
/* But the EDITED entries are used later */
|
|
|
|
if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
|
2016-08-09 18:26:43 +00:00
|
|
|
{
|
|
|
|
InputList_RemoveNode(pCurrent);
|
|
|
|
}
|
|
|
|
}
|
2016-08-08 20:30:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
/* Add the DEFAULT entry and set font substitutes */
|
2016-08-08 20:30:36 +00:00
|
|
|
for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
|
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
|
|
|
|
continue;
|
|
|
|
|
2016-08-09 18:26:43 +00:00
|
|
|
if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
2017-11-13 01:36:52 +00:00
|
|
|
bRet = InputList_SetFontSubstitutes(pCurrent->pLocale->dwId);
|
2022-09-15 23:09:37 +00:00
|
|
|
InputList_AddInputMethodToUserRegistry(hPreloadKey, hSubstKey, 1, pCurrent);
|
2022-10-28 22:35:19 +00:00
|
|
|
|
|
|
|
/* Activate the DEFAULT entry */
|
|
|
|
ActivateKeyboardLayout(pCurrent->hkl, KLF_RESET);
|
2016-08-08 20:30:36 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
/* Add entries except DEFAULT to registry */
|
|
|
|
dwNumber = 2;
|
|
|
|
for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
|
|
|
|
continue;
|
|
|
|
if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
|
|
|
|
continue;
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
InputList_AddInputMethodToUserRegistry(hPreloadKey, hSubstKey, dwNumber, pCurrent);
|
|
|
|
|
|
|
|
++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, &pCurrent->hkl, 0))
|
|
|
|
{
|
2022-10-28 22:35:19 +00:00
|
|
|
DWORD dwRecipients = BSM_ALLDESKTOPS | BSM_APPLICATIONS;
|
2016-08-08 20:30:36 +00:00
|
|
|
|
|
|
|
BroadcastSystemMessageW(BSF_POSTMESSAGE,
|
|
|
|
&dwRecipients,
|
|
|
|
WM_INPUTLANGCHANGEREQUEST,
|
2022-10-28 22:35:19 +00:00
|
|
|
INPUTLANGCHANGE_SYSCHARSET,
|
2016-08-08 20:30:36 +00:00
|
|
|
(LPARAM)pCurrent->hkl);
|
|
|
|
}
|
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
/* Retry to delete (in case of failure to delete the default keyboard) */
|
2016-08-09 14:01:52 +00:00
|
|
|
for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
if (pCurrent->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
|
|
|
|
{
|
|
|
|
UnloadKeyboardLayout(pCurrent->hkl);
|
|
|
|
InputList_RemoveNode(pCurrent);
|
|
|
|
}
|
2016-08-08 20:30:36 +00:00
|
|
|
}
|
2017-11-13 01:36:52 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
RegCloseKey(hPreloadKey);
|
|
|
|
RegCloseKey(hSubstKey);
|
2017-11-13 01:36:52 +00:00
|
|
|
return bRet;
|
2016-08-08 20:30:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-09 18:26:43 +00:00
|
|
|
BOOL
|
2016-08-08 20:30:36 +00:00
|
|
|
InputList_Add(LOCALE_LIST_NODE *pLocale, LAYOUT_LIST_NODE *pLayout)
|
|
|
|
{
|
2016-08-09 14:01:52 +00:00
|
|
|
WCHAR szIndicator[MAX_STR_LEN];
|
2022-09-15 23:09:37 +00:00
|
|
|
INPUT_LIST_NODE *pInput = NULL;
|
2016-08-08 20:30:36 +00:00
|
|
|
|
|
|
|
if (pLocale == NULL || pLayout == NULL)
|
|
|
|
{
|
2016-08-09 18:26:43 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (pInput = _InputList; pInput != NULL; pInput = pInput->pNext)
|
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
if (pInput->wFlags & INPUT_LIST_NODE_FLAG_DELETED)
|
|
|
|
continue;
|
|
|
|
|
2016-08-09 18:26:43 +00:00
|
|
|
if (pInput->pLocale == pLocale && pInput->pLayout == pLayout)
|
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
return FALSE; /* Already exists */
|
2016-08-09 18:26:43 +00:00
|
|
|
}
|
2016-08-08 20:30:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pInput = InputList_AppendNode();
|
2016-08-09 18:26:43 +00:00
|
|
|
pInput->wFlags = INPUT_LIST_NODE_FLAG_ADDED;
|
2016-08-08 20:30:36 +00:00
|
|
|
pInput->pLocale = pLocale;
|
|
|
|
pInput->pLayout = pLayout;
|
2016-08-09 14:01:52 +00:00
|
|
|
|
|
|
|
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;
|
2016-08-18 10:31:42 +00:00
|
|
|
pInput->pszIndicator = _wcsdup(szIndicator);
|
2016-08-09 14:01:52 +00:00
|
|
|
}
|
|
|
|
}
|
2016-08-09 18:26:43 +00:00
|
|
|
|
|
|
|
return TRUE;
|
2016-08-09 14:01:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
VOID
|
|
|
|
InputList_SetDefault(INPUT_LIST_NODE *pNode)
|
|
|
|
{
|
|
|
|
INPUT_LIST_NODE *pCurrent;
|
|
|
|
|
|
|
|
if (pNode == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (pCurrent = _InputList; pCurrent != NULL; pCurrent = pCurrent->pNext)
|
|
|
|
{
|
|
|
|
if (pCurrent == pNode)
|
|
|
|
{
|
2016-08-09 18:26:43 +00:00
|
|
|
pCurrent->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
|
2016-08-09 14:01:52 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-08-09 18:26:43 +00:00
|
|
|
pCurrent->wFlags &= ~INPUT_LIST_NODE_FLAG_DEFAULT;
|
2016-08-09 14:01:52 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
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;
|
|
|
|
}
|
2016-08-09 14:01:52 +00:00
|
|
|
|
2016-08-09 18:26:43 +00:00
|
|
|
/*
|
|
|
|
* It marks the input method for deletion, but does not delete it directly.
|
|
|
|
* To apply the changes using InputList_Process()
|
|
|
|
*/
|
2022-09-28 22:31:43 +00:00
|
|
|
BOOL
|
2016-08-09 14:01:52 +00:00
|
|
|
InputList_Remove(INPUT_LIST_NODE *pNode)
|
|
|
|
{
|
2022-09-28 22:31:43 +00:00
|
|
|
BOOL ret = FALSE;
|
2016-08-09 18:26:43 +00:00
|
|
|
BOOL bRemoveNode = FALSE;
|
|
|
|
|
2016-08-09 14:01:52 +00:00
|
|
|
if (pNode == NULL)
|
2022-09-28 22:31:43 +00:00
|
|
|
return FALSE;
|
2016-08-09 14:01:52 +00:00
|
|
|
|
2016-08-09 18:26:43 +00:00
|
|
|
if (pNode->wFlags & INPUT_LIST_NODE_FLAG_ADDED)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* If the input method has been added to the list, but not yet written
|
|
|
|
* in the registry, then simply remove it from the list
|
|
|
|
*/
|
|
|
|
bRemoveNode = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
pNode->wFlags |= INPUT_LIST_NODE_FLAG_DELETED;
|
2016-08-09 18:26:43 +00:00
|
|
|
}
|
2016-08-09 14:01:52 +00:00
|
|
|
|
2016-08-09 18:26:43 +00:00
|
|
|
if (pNode->wFlags & INPUT_LIST_NODE_FLAG_DEFAULT)
|
2016-08-09 14:01:52 +00:00
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
INPUT_LIST_NODE *pCurrent = InputList_FindNextDefault(pNode);
|
|
|
|
if (pCurrent)
|
|
|
|
pCurrent->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
|
|
|
|
|
|
|
|
pNode->wFlags &= ~INPUT_LIST_NODE_FLAG_DEFAULT;
|
2022-09-28 22:31:43 +00:00
|
|
|
ret = TRUE; /* default input is changed */
|
2016-08-09 14:01:52 +00:00
|
|
|
}
|
2016-08-09 18:26:43 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
if (bRemoveNode)
|
2016-08-09 18:26:43 +00:00
|
|
|
{
|
|
|
|
InputList_RemoveNode(pNode);
|
|
|
|
}
|
2022-09-28 22:31:43 +00:00
|
|
|
|
|
|
|
return ret;
|
2016-08-08 20:30:36 +00:00
|
|
|
}
|
|
|
|
|
2022-09-28 22:31:43 +00:00
|
|
|
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;
|
|
|
|
}
|
2016-08-08 20:30:36 +00:00
|
|
|
|
|
|
|
VOID
|
|
|
|
InputList_Create(VOID)
|
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
INT iLayoutCount, iIndex;
|
|
|
|
WCHAR szIndicator[MAX_STR_LEN];
|
|
|
|
INPUT_LIST_NODE *pInput;
|
|
|
|
HKL *pLayoutList, hklDefault;
|
|
|
|
|
|
|
|
SystemParametersInfoW(SPI_GETDEFAULTINPUTLANG, 0, &hklDefault, 0);
|
2016-08-08 20:30:36 +00:00
|
|
|
|
|
|
|
iLayoutCount = GetKeyboardLayoutList(0, NULL);
|
2016-08-09 18:26:43 +00:00
|
|
|
pLayoutList = (HKL*) malloc(iLayoutCount * sizeof(HKL));
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
if (!pLayoutList || GetKeyboardLayoutList(iLayoutCount, pLayoutList) <= 0)
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
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? */
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
pInput->wFlags |= INPUT_LIST_NODE_FLAG_DEFAULT;
|
|
|
|
hklDefault = NULL; /* No more default item */
|
|
|
|
}
|
2016-08-08 20:30:36 +00:00
|
|
|
|
2022-09-15 23:09:37 +00:00
|
|
|
/* 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)
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
2022-09-15 23:09:37 +00:00
|
|
|
szIndicator[len - 1] = 0;
|
|
|
|
pInput->pszIndicator = _wcsdup(szIndicator);
|
2016-08-08 20:30:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-09-15 23:09:37 +00:00
|
|
|
|
|
|
|
free(pLayoutList);
|
2016-08-08 20:30:36 +00:00
|
|
|
}
|
|
|
|
|
2022-09-28 22:31:43 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
2016-08-08 20:30:36 +00:00
|
|
|
|
|
|
|
INPUT_LIST_NODE*
|
2016-08-09 14:01:52 +00:00
|
|
|
InputList_GetFirst(VOID)
|
2016-08-08 20:30:36 +00:00
|
|
|
{
|
|
|
|
return _InputList;
|
|
|
|
}
|