/**************************************************************************\ * Module Name: imectl.c * * Copyright (c) 1985 - 1999, Microsoft Corporation * * IME Window Handling Routines * * History: * 20-Dec-1995 wkwok \**************************************************************************/ #include "precomp.h" #pragma hdrstop #include #define LATE_CREATEUI 1 CONST WCHAR szIndicDLL[] = L"indicdll.dll"; FARPROC gpfnGetIMEMenuItemData = NULL; BOOL IMEIndicatorGetMenuIDData(PUINT puMenuID, PDWORD pdwData); /* * Local Routines. */ LONG ImeWndCreateHandler(PIMEUI, LPCREATESTRUCT); void ImeWndDestroyHandler(PIMEUI); LRESULT ImeSystemHandler(PIMEUI, UINT, WPARAM, LPARAM); LONG ImeSelectHandler(PIMEUI, UINT, WPARAM, LPARAM); LRESULT ImeControlHandler(PIMEUI, UINT, WPARAM, LPARAM, BOOL); LRESULT ImeSetContextHandler(PIMEUI, UINT, WPARAM, LPARAM); LRESULT ImeNotifyHandler(PIMEUI, UINT, WPARAM, LPARAM); HWND CreateIMEUI(PIMEUI, HKL); VOID DestroyIMEUI(PIMEUI); LRESULT SendMessageToUI(PIMEUI, UINT, WPARAM, LPARAM, BOOL); VOID SendOpenStatusNotify(PIMEUI, HWND, BOOL); VOID ImeSetImc(PIMEUI, HIMC); VOID FocusSetIMCContext(HWND, BOOL); BOOL ImeBroadCastMsg(PIMEUI, UINT, WPARAM, LPARAM); VOID ImeMarkUsedContext(HWND, HIMC); BOOL ImeIsUsableContext(HWND, HIMC); BOOL GetIMEShowStatus(void); /* * Common macros for IME UI, HKL and IMC handlings */ #define GETHKL(pimeui) (KHKL_TO_HKL(pimeui->hKL)) #define SETHKL(pimeui, hkl) (pimeui->hKL=(hkl)) #define GETIMC(pimeui) (KHIMC_TO_HIMC(pimeui->hIMC)) #define SETIMC(pimeui, himc) (ImeSetImc(pimeui, KHIMC_TO_HIMC(himc))) #define GETUI(pimeui) (KHWND_TO_HWND(pimeui->hwndUI)) #define SETUI(pimeui, hwndui) (pimeui->hwndUI=(hwndui)) LOOKASIDE ImeUILookaside; /***************************************************************************\ * NtUserBroadcastImeShowStatusChange(), NtUserCheckImeShowStatusInThread() * * Stub for kernel mode routines * \***************************************************************************/ _inline void NtUserBroadcastImeShowStatusChange(HWND hwndDefIme, BOOL fShow) { NtUserCallHwndParamLock(hwndDefIme, fShow, SFI_XXXBROADCASTIMESHOWSTATUSCHANGE); } _inline void NtUserCheckImeShowStatusInThread(HWND hwndDefIme) { NtUserCallHwndLock(hwndDefIme, SFI_XXXCHECKIMESHOWSTATUSINTHREAD); } /***************************************************************************\ * ImeWndProc * * WndProc for IME class * * History: \***************************************************************************/ LRESULT APIENTRY ImeWndProcWorker( PWND pwnd, UINT message, WPARAM wParam, LPARAM lParam, DWORD fAnsi) { HWND hwnd = HWq(pwnd); PIMEUI pimeui; static BOOL fInit = TRUE; CheckLock(pwnd); VALIDATECLASSANDSIZE(pwnd, FNID_IME); INITCONTROLLOOKASIDE(&ImeUILookaside, IMEUI, spwnd, 8); #ifdef CUAS_ENABLE if (IS_CICERO_ENABLED()) { LRESULT lRet; lRet = fpCtfImmDispatchDefImeMessage(hwnd, message, wParam, lParam); if (lRet) return lRet; } #endif /* * If the control is not interested in this message, * pass it to DefWindowProc. */ if (!FWINDOWMSG(message, FNID_IME)) return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi); /* * Get the pimeui for the given window now since we will use it a lot in * various handlers. This was stored using SetWindowLong(hwnd,0,pimeui) when * we initially created the IME control. */ pimeui = ((PIMEWND)pwnd)->pimeui; if (pimeui == NULL) { /* * Further processing not needed */ RIPMSG0(RIP_WARNING, "ImeWndProcWorker: pimeui == NULL\n"); return 0L; } /* * This is necessary to avoid recursion call from IME UI. */ UserAssert(pimeui->nCntInIMEProc >= 0); if (pimeui->nCntInIMEProc > 0) { TAGMSG5(DBGTAG_IMM, "ImeWndProcWorker: Recursive for pwnd=%08p, msg=%08x, wp=%08x, lp=%08x, fAnsi=%d\n", pwnd, message, wParam, lParam, fAnsi); switch (message) { case WM_IME_SYSTEM: switch (wParam) { case IMS_ISACTIVATED: case IMS_SETOPENSTATUS: // case IMS_SETCONVERSIONSTATUS: case IMS_SETSOFTKBDONOFF: /* * Because these will not be pass to UI. * We can do it. */ break; default: return 0L; } break; case WM_IME_STARTCOMPOSITION: case WM_IME_ENDCOMPOSITION: case WM_IME_COMPOSITION: case WM_IME_SETCONTEXT: #if !defined(CUAS_ENABLE) case WM_IME_NOTIFY: #endif case WM_IME_CONTROL: case WM_IME_COMPOSITIONFULL: case WM_IME_SELECT: case WM_IME_CHAR: case WM_IME_REQUEST: return 0L; #ifdef CUAS_ENABLE case WM_IME_NOTIFY: if (wParam >= IMN_PRIVATE && ! IS_IME_KBDLAYOUT(GETHKL(pimeui)) && IS_CICERO_ENABLED()) { // // CUAS: IMN_PRIVATE always call UI window. // break; } return 0L; #endif default: return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi); } } if (TestWF(pwnd, WFINDESTROY) || TestWF(pwnd, WFDESTROYED)) { switch (message) { case WM_DESTROY: case WM_NCDESTROY: case WM_FINALDESTROY: break; default: RIPMSG1(RIP_WARNING, "ImeWndProcWorker: message %x is sent to destroyed window.", message); return 0L; } } switch (message) { case WM_ERASEBKGND: return (LONG)TRUE; case WM_PAINT: break; case WM_CREATE: return ImeWndCreateHandler(pimeui, (LPCREATESTRUCT)lParam); case WM_DESTROY: /* * We are destroying the IME window, destroy * any UI window that it owns. */ ImeWndDestroyHandler(pimeui); break; case WM_NCDESTROY: case WM_FINALDESTROY: if (pimeui) { Unlock(&pimeui->spwnd); FreeLookasideEntry(&ImeUILookaside, pimeui); } NtUserSetWindowFNID(hwnd, FNID_CLEANEDUP_BIT); goto CallDWP; case WM_IME_SYSTEM: UserAssert(pimeui->spwnd == pwnd); return ImeSystemHandler(pimeui, message, wParam, lParam); case WM_IME_SELECT: return ImeSelectHandler(pimeui, message, wParam, lParam); case WM_IME_CONTROL: return ImeControlHandler(pimeui, message, wParam, lParam, fAnsi); case WM_IME_SETCONTEXT: return ImeSetContextHandler(pimeui, message, wParam, lParam); case WM_IME_NOTIFY: return ImeNotifyHandler(pimeui, message, wParam, lParam); case WM_IME_REQUEST: return 0; case WM_IME_COMPOSITION: case WM_IME_ENDCOMPOSITION: case WM_IME_STARTCOMPOSITION: return SendMessageToUI(pimeui, message, wParam, lParam, fAnsi); default: CallDWP: return DefWindowProcWorker(pwnd, message, wParam, lParam, fAnsi); } return 0L; } LRESULT WINAPI ImeWndProcA( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { PWND pwnd; if ((pwnd = ValidateHwnd(hwnd)) == NULL) { return (0L); } return ImeWndProcWorker(pwnd, message, wParam, lParam, TRUE); } LRESULT WINAPI ImeWndProcW( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { PWND pwnd; if ((pwnd = ValidateHwnd(hwnd)) == NULL) { return (0L); } return ImeWndProcWorker(pwnd, message, wParam, lParam, FALSE); } LONG ImeWndCreateHandler( PIMEUI pimeui, LPCREATESTRUCT lpcs) { PWND pwndParent; HIMC hImc; PWND pwndIme = pimeui->spwnd; #if _DBG static DWORD dwFirstWinlogonThreadId; #endif #if _DBG /* * For Winlogon, only the first thread can have IME processing. */ if (gfLogonProcess) { UserAssert(dwFirstWinLogonThreadId == 0); dwFirstWinlogonThreadId = GetCurrentThreadId(); } #endif if (!TestWF(pwndIme, WFPOPUP) || !TestWF(pwndIme, WFDISABLED)) { RIPMSG0(RIP_WARNING, "IME window should have WS_POPUP and WS_DISABLE!!"); return -1L; } /* * Check with parent window, if exists, try to get IMC. * If this is top level window, wait for first WM_IME_SETCONTEXT. */ if ((pwndParent = ValidateHwndNoRip(lpcs->hwndParent)) != NULL) { hImc = KHIMC_TO_HIMC(pwndParent->hImc); if (hImc != NULL_HIMC && ImeIsUsableContext(HWq(pwndIme), hImc)) { /* * Store it for later use. */ SETIMC(pimeui, hImc); } else { SETIMC(pimeui, NULL_HIMC); } } else { SETIMC(pimeui, NULL_HIMC); } /* * Initialize status window show state * The status window is not open yet. */ pimeui->fShowStatus = 0; pimeui->nCntInIMEProc = 0; pimeui->fActivate = 0; pimeui->fDestroy = 0; pimeui->hwndIMC = NULL; pimeui->hKL = THREAD_HKL(); pimeui->fCtrlShowStatus = TRUE; #if !defined(CUAS_ENABLE) // move to LoadThreadLayout /* * Load up the IME DLL of current keyboard layout. */ fpImmLoadIME(GETHKL(pimeui)); #ifdef LATE_CREATEUI SETUI(pimeui, NULL); #else SETUI(pimeui, CreateIMEUI(pimeui, pimeui->hKL)); #endif #else // Cicero pimeui->dwPrevToolbarStatus = 0; #endif // CUAS_ENABLE // move to LoadThreadLayout return 0L; } void ImeWndDestroyHandler( PIMEUI pimeui) { DestroyIMEUI(pimeui); } #ifdef CUAS_ENABLE VOID CtfLoadThreadLayout( PIMEUI pimeui) { #if 1 /* * Cicero Unaware Support. Activate Thread Input Manager. */ fpCtfImmTIMActivate(pimeui->hKL); /* * Load up the IME DLL of current keyboard layout. */ pimeui->hKL = THREAD_HKL(); // Reload thread hKL if TIM activated, hKL changed. fpImmLoadIME(GETHKL(pimeui)); #ifdef LATE_CREATEUI SETUI(pimeui, NULL); #else SETUI(pimeui, CreateIMEUI(pimeui, pimeui->hKL)); #endif #else UNREFERENCED_PARAMETER(pimeui); #endif } #endif // CUAS_ENABLE /***************************************************************************\ * GetSystemModulePath * \***************************************************************************/ #define SYSTEM_DIR 0 #define WINDOWS_DIR 1 UINT GetSystemModulePath(DWORD dir, LPWSTR psz, DWORD cch, CONST WCHAR* pszModuleName) { UINT uRet; if (! psz || ! pszModuleName || cch == 0) { return 0; } if (! pszModuleName[0]) { return 0; } if (dir == SYSTEM_DIR) { uRet = GetSystemDirectory(psz, cch); } else { uRet = GetSystemWindowsDirectory(psz, cch); } if (uRet >= cch) { uRet = 0; psz[0] = L'\0'; } else if (uRet) { UINT uLen; if (psz[uRet - 1] != L'\\') { psz[uRet] = L'\\'; uRet++; } if (uRet >= cch) { uRet = 0; psz[0] = L'\0'; } else { uLen = wcslen(pszModuleName); if (cch - uRet > uLen) { wcsncpy(&psz[uRet], pszModuleName, cch - uRet); uRet += uLen; } else { uRet = 0; psz[0] = L'\0'; } } } return uRet; } /***************************************************************************\ * ImeRunHelp * * Display Help file (HLP and CHM). * * History: * 27-Oct-98 Hiroyama \***************************************************************************/ void ImeRunHelp(LPWSTR wszHelpFile) { static const WCHAR wszHelpFileExt[] = L".HLP"; UINT cchLen = wcslen(wszHelpFile); if (cchLen > 4 && _wcsicmp(wszHelpFile + cchLen - 4, wszHelpFileExt) == 0) { #ifdef FYI WinHelpW(NULL, wszHelpFile, HELP_CONTENTS, 0); #else WinHelpW(NULL, wszHelpFile, HELP_FINDER, 0); #endif } else { // // If it's not HLP file, try to run hh.exe, HTML based // help tool. It should be in %windir%\hh.exe. // static const WCHAR wszHH[] = L"hh.exe "; WCHAR wszCmdLine[MAX_PATH * 2]; DWORD idProcess; STARTUPINFO StartupInfo; PROCESS_INFORMATION ProcessInformation; UINT i; WCHAR wszAppName[MAX_PATH + 1]; if (! (i=GetSystemModulePath(WINDOWS_DIR, wszAppName, ARRAYSIZE(wszAppName), wszHH))) { return; } if (i + cchLen >= ARRAYSIZE(wszCmdLine)) { return; } wcsncpy(wszCmdLine, wszAppName, ARRAYSIZE(wszCmdLine)); wcsncpy(&wszCmdLine[i], wszHelpFile, ARRAYSIZE(wszCmdLine) - i); /* * Launch HTML Help. */ RtlZeroMemory(&StartupInfo, sizeof(StartupInfo)); StartupInfo.cb = sizeof(StartupInfo); StartupInfo.wShowWindow = SW_SHOW; StartupInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_FORCEONFEEDBACK; TAGMSG2(DBGTAG_IMM, "Invoking help with '%S %S'", wszAppName, wszHelpFile); idProcess = (DWORD)CreateProcessW(wszAppName, wszCmdLine, NULL, NULL, FALSE, NORMAL_PRIORITY_CLASS, NULL, NULL, &StartupInfo, &ProcessInformation); if (idProcess) { WaitForInputIdle(ProcessInformation.hProcess, 10000); NtClose(ProcessInformation.hProcess); NtClose(ProcessInformation.hThread); } } } LRESULT ImeSystemHandler( PIMEUI pimeui, UINT message, WPARAM wParam, LPARAM lParam) { PINPUTCONTEXT pInputContext; HIMC hImc = GETIMC(pimeui); LRESULT dwRet = 0L; UNREFERENCED_PARAMETER(message); switch (wParam) { case IMS_SETOPENCLOSE: if (hImc != NULL_HIMC) fpImmSetOpenStatus(hImc, (BOOL)lParam); break; case IMS_WINDOWPOS: if (hImc != NULL_HIMC) { BOOL f31Hidden = FALSE; if ((pInputContext = fpImmLockIMC(hImc)) != NULL) { f31Hidden = (pInputContext->fdw31Compat & F31COMPAT_MCWHIDDEN); fpImmUnlockIMC(hImc); } if (IsWindow(KHWND_TO_HWND(pimeui->hwndIMC))) { int i; if (!f31Hidden) { COMPOSITIONFORM cof; if (fpImmGetCompositionWindow(hImc, &cof) && cof.dwStyle != CFS_DEFAULT) { fpImmSetCompositionWindow(hImc, &cof); } } for (i = 0; i < 4 ; i++) { CANDIDATEFORM caf; if (fpImmGetCandidateWindow(hImc, (DWORD)i, &caf) && caf.dwStyle != CFS_DEFAULT) { fpImmSetCandidateWindow(hImc, &caf); } } } } break; case IMS_ACTIVATECONTEXT: FocusSetIMCContext((HWND)(lParam), TRUE); break; case IMS_DEACTIVATECONTEXT: FocusSetIMCContext((HWND)(lParam), FALSE); break; #ifdef CUAS_ENABLE case IMS_LOADTHREADLAYOUT: CtfLoadThreadLayout(pimeui); break; #endif // CUAS_ENABLE case IMS_UNLOADTHREADLAYOUT: return (LONG)(fpImmFreeLayout((DWORD)lParam)); case IMS_ACTIVATETHREADLAYOUT: return (LONG)(fpImmActivateLayout((HKL)lParam)); case IMS_SETCANDIDATEPOS: if ( (pInputContext = fpImmLockIMC( hImc )) != NULL ) { LPCANDIDATEFORM lpcaf; DWORD dwIndex = (DWORD)lParam; lpcaf = &(pInputContext->cfCandForm[dwIndex]); fpImmSetCandidateWindow( hImc, lpcaf ); fpImmUnlockIMC( hImc ); } break; case IMS_SETCOMPOSITIONWINDOW: if ( (pInputContext = fpImmLockIMC( hImc )) != NULL ) { LPCOMPOSITIONFORM lpcof; lpcof = &(pInputContext->cfCompForm); pInputContext->fdw31Compat |= F31COMPAT_CALLFROMWINNLS; fpImmSetCompositionWindow( hImc, lpcof); fpImmUnlockIMC( hImc ); } break; case IMS_SETCOMPOSITIONFONT: if ( (pInputContext = fpImmLockIMC( hImc )) != NULL ) { LPLOGFONT lplf; lplf = &(pInputContext->lfFont.W); fpImmSetCompositionFont( hImc, lplf ); fpImmUnlockIMC( hImc ); } break; case IMS_CONFIGUREIME: fpImmConfigureIMEW( (HKL)lParam, KHWND_TO_HWND(pimeui->hwndIMC), IME_CONFIG_GENERAL, NULL); break; case IMS_CHANGE_SHOWSTAT: // Private message from internat.exe // Before it reaches here, the registry is already updated. if (GetIMEShowStatus() == !lParam) { #if 1 NtUserBroadcastImeShowStatusChange(HW(pimeui->spwnd), !!lParam); #else SystemParametersInfo(SPI_SETSHOWIMEUI, lParam, NULL, FALSE); #endif } break; case IMS_GETCONVERSIONMODE: { DWORD dwConv = 0; DWORD dwTemp; fpImmGetConversionStatus(hImc, &dwConv, &dwTemp); return (dwConv); break; } case IMS_SETSOFTKBDONOFF: fpImmEnumInputContext(0, SyncSoftKbdState, lParam); break; case IMS_GETIMEMENU: // new in NT50 // IMS_GETIEMMENU is used to handle Inter Process GetMenu. // NOTE: This operation is only intended to internat.exe return fpImmPutImeMenuItemsIntoMappedFile((HIMC)lParam); case IMS_IMEHELP: dwRet = IME_ESC_GETHELPFILENAME; dwRet = fpImmEscapeW(GETHKL(pimeui), GETIMC(pimeui), IME_ESC_QUERY_SUPPORT, (LPVOID)&dwRet); if (lParam) { // try to run WinHelp WCHAR wszHelpFile[MAX_PATH]; if (dwRet) { if (fpImmEscapeW(GETHKL(pimeui), GETIMC(pimeui), IME_ESC_GETHELPFILENAME, (LPVOID)wszHelpFile)) { ImeRunHelp(wszHelpFile); } } } return dwRet; case IMS_GETCONTEXT: dwRet = (ULONG_PTR)fpImmGetContext((HWND)lParam); return dwRet; case IMS_ENDIMEMENU: // New in NT5.0: Special support for Internat.exe if (IsWindow((HWND)lParam)) { HIMC hImc; UINT uID; DWORD dwData; hImc = fpImmGetContext((HWND)lParam); if (hImc != NULL) { // // Call Indicator to get IME menu data. // if (IMEIndicatorGetMenuIDData(&uID, &dwData)) { fpImmNotifyIME(hImc, NI_IMEMENUSELECTED, uID, dwData); } fpImmReleaseContext((HWND)lParam, hImc); } } break; case IMS_SENDNOTIFICATION: case IMS_FINALIZE_COMPSTR: dwRet = fpImmSystemHandler(hImc, wParam, lParam); break; #ifdef CUAS_ENABLE case IMS_SETLANGBAND: case IMS_RESETLANGBAND: dwRet = fpImmSystemHandler(hImc, wParam, lParam); break; #endif // CUAS_ENABLE default: break; } return dwRet; } LONG ImeSelectHandler( PIMEUI pimeui, UINT message, WPARAM wParam, LPARAM lParam) { HWND hwndUI; /* * Deliver this message to other IME windows in this thread. */ if (pimeui->fDefault) ImeBroadCastMsg(pimeui, message, wParam, lParam); /* * We must re-create UI window of newly selected IME. */ if ((BOOL)wParam == TRUE) { UserAssert(!IsWindow(GETUI(pimeui))); SETHKL(pimeui, (HKL)lParam); #ifdef LATE_CREATEUI if (!pimeui->fActivate) return 0L; #endif hwndUI = CreateIMEUI(pimeui, (HKL)lParam); SETUI(pimeui, hwndUI); if (hwndUI != NULL) { SetWindowLongPtr(hwndUI, IMMGWLP_IMC, (LONG_PTR)GETIMC(pimeui)); SendMessageToUI(pimeui, message, wParam, lParam, FALSE); } if (GetIMEShowStatus() && pimeui->fCtrlShowStatus) { if (!pimeui->fShowStatus && pimeui->fActivate && IsWindow(KHWND_TO_HWND(pimeui->hwndIMC))) { /* * This must be sent to an application as an app may want * to hook this message to do its own UI. */ SendOpenStatusNotify(pimeui, KHWND_TO_HWND(pimeui->hwndIMC), TRUE); } } } else { if (pimeui->fShowStatus && pimeui->fActivate && IsWindow(KHWND_TO_HWND(pimeui->hwndIMC))) { /* * This must be sent to an application as an app may want * to hook this message to do its own UI. */ SendOpenStatusNotify(pimeui, KHWND_TO_HWND(pimeui->hwndIMC), FALSE); } SendMessageToUI(pimeui, message, wParam, lParam, FALSE); DestroyIMEUI(pimeui); SETHKL(pimeui, (HKL)NULL); } return 0L; } LRESULT ImeControlHandler( PIMEUI pimeui, UINT message, WPARAM wParam, LPARAM lParam, BOOL fAnsi) { HIMC hImc; DWORD dwConversion, dwSentence; #ifdef CUAS_ENABLE if (IS_CICERO_ENABLED()) { if (wParam == IMC_OPENSTATUSWINDOW) { fpCtfImmRestoreToolbarWnd(pimeui->dwPrevToolbarStatus); pimeui->dwPrevToolbarStatus = 0; } else if (wParam == IMC_CLOSESTATUSWINDOW) { pimeui->dwPrevToolbarStatus = fpCtfImmHideToolbarWnd(); } } #endif // CUAS_ENABLE /* * Do nothing with NULL hImc. */ if ((hImc = GETIMC(pimeui)) == NULL_HIMC) return 0L; switch (wParam) { case IMC_OPENSTATUSWINDOW: if (GetIMEShowStatus() && !pimeui->fShowStatus) { pimeui->fShowStatus = TRUE; SendMessageToUI(pimeui, WM_IME_NOTIFY, IMN_OPENSTATUSWINDOW, 0L, FALSE); } pimeui->fCtrlShowStatus = TRUE; break; case IMC_CLOSESTATUSWINDOW: if (GetIMEShowStatus() && pimeui->fShowStatus) { pimeui->fShowStatus = FALSE; SendMessageToUI(pimeui, WM_IME_NOTIFY, IMN_CLOSESTATUSWINDOW, 0L, FALSE); } pimeui->fCtrlShowStatus = FALSE; break; /* * ------------------------------------------------ * IMC_SETCOMPOSITIONFONT, * IMC_SETCONVERSIONMODE, * IMC_SETOPENSTATUS * ------------------------------------------------ * Don't pass these WM_IME_CONTROLs to UI window. * Call Imm in order to process these requests instead. * It makes message flows simpler. */ case IMC_SETCOMPOSITIONFONT: if (fAnsi) { if (!fpImmSetCompositionFontA(hImc, (LPLOGFONTA)lParam)) return 1L; } else { if (!fpImmSetCompositionFontW(hImc, (LPLOGFONTW)lParam)) return 1L; } break; case IMC_SETCONVERSIONMODE: if (!fpImmGetConversionStatus(hImc, &dwConversion, &dwSentence) || !fpImmSetConversionStatus(hImc, (DWORD)lParam, dwSentence)) return 1L; break; case IMC_SETSENTENCEMODE: if (!fpImmGetConversionStatus(hImc, &dwConversion, &dwSentence) || !fpImmSetConversionStatus(hImc, dwConversion, (DWORD)lParam)) return 1L; break; case IMC_SETOPENSTATUS: if (!fpImmSetOpenStatus(hImc, (BOOL)lParam)) return 1L; break; case IMC_GETCONVERSIONMODE: if (!fpImmGetConversionStatus(hImc,&dwConversion, &dwSentence)) return 1L; return (LONG)dwConversion; case IMC_GETSENTENCEMODE: if (!fpImmGetConversionStatus(hImc,&dwConversion, &dwSentence)) return 1L; return (LONG)dwSentence; case IMC_GETOPENSTATUS: return (LONG)fpImmGetOpenStatus(hImc); case IMC_GETCOMPOSITIONFONT: if (fAnsi) { if (!fpImmGetCompositionFontA(hImc, (LPLOGFONTA)lParam)) return 1L; } else { if (!fpImmGetCompositionFontW(hImc, (LPLOGFONTW)lParam)) return 1L; } break; case IMC_SETCOMPOSITIONWINDOW: if (!fpImmSetCompositionWindow(hImc, (LPCOMPOSITIONFORM)lParam)) return 1L; break; case IMC_SETSTATUSWINDOWPOS: { POINT ppt; ppt.x = (LONG)((LPPOINTS)&lParam)->x; ppt.y = (LONG)((LPPOINTS)&lParam)->y; if (!fpImmSetStatusWindowPos(hImc, &ppt)) return 1L; } break; case IMC_SETCANDIDATEPOS: if (!fpImmSetCandidateWindow(hImc, (LPCANDIDATEFORM)lParam)) return 1; break; /* * Followings are the messsages to be sent to UI. */ case IMC_GETCANDIDATEPOS: case IMC_GETSTATUSWINDOWPOS: case IMC_GETCOMPOSITIONWINDOW: case IMC_GETSOFTKBDPOS: case IMC_SETSOFTKBDPOS: return SendMessageToUI(pimeui, message, wParam, lParam, fAnsi); default: break; } return 0L; } LRESULT ImeSetContextHandler( PIMEUI pimeui, UINT message, WPARAM wParam, LPARAM lParam) { HWND hwndPrevIMC, hwndFocus; // focus window in the thread HIMC hFocusImc; // focus window's IMC LRESULT lRet; pimeui->fActivate = (BOOL)wParam ? 1 : 0; hwndPrevIMC = KHWND_TO_HWND(pimeui->hwndIMC); if (wParam) { /* * if it's being activated */ #ifdef LATE_CREATEUI if (!GETUI(pimeui)) SETUI(pimeui, CreateIMEUI(pimeui, GETHKL(pimeui))); #endif /* * Check if this process a console IME ? */ if (gfConIme == UNKNOWN_CONIME) { gfConIme = (DWORD)NtUserGetThreadState(UserThreadStateIsConImeThread); if (gfConIme) { RIPMSG0(RIP_VERBOSE, "ImmSetContextHandler: This thread is console IME.\n"); UserAssert(pimeui); // Console IME will never show the IME status window. pimeui->fCtrlShowStatus = FALSE; } } if (gfConIme) { /* * Special handling for Console IME is needed */ PWND pwndOwner; UserAssert(pimeui->spwnd); pwndOwner = REBASEPWND(pimeui->spwnd, spwndOwner); if (pwndOwner != NULL) { /* * Set current associated hIMC in IMEUI. */ SETIMC(pimeui, pwndOwner->hImc); /* * Store it to the window memory. */ if (GETUI(pimeui) != NULL) SetWindowLongPtr(GETUI(pimeui), IMMGWLP_IMC, (LONG_PTR)pwndOwner->hImc); } hwndFocus = NtUserQueryWindow(HW(pimeui->spwnd), WindowFocusWindow); hFocusImc = KHIMC_TO_HIMC(pwndOwner->hImc); RIPMSG2(RIP_VERBOSE, "CONSOLE IME: hwndFocus = %x, hFocusImc = %x", hwndFocus, hFocusImc); return SendMessageToUI(pimeui, message, wParam, lParam, FALSE); } else { hwndFocus = NtUserQueryWindow(HW(pimeui->spwnd), WindowFocusWindow); hFocusImc = fpImmGetContext(hwndFocus); } /* * Cannot share input context with other IME window. */ if (hFocusImc != NULL_HIMC && !ImeIsUsableContext(HW(pimeui->spwnd), hFocusImc)) { SETIMC(pimeui, NULL_HIMC); return 0L; } SETIMC(pimeui, hFocusImc); /* * Store it to the window memory. */ if (GETUI(pimeui) != NULL) SetWindowLongPtr(GETUI(pimeui), IMMGWLP_IMC, (LONG_PTR)hFocusImc); /* * When we're receiving context, * it is necessary to set the owner to this window. * This is for: * Give the UI moving information. * Give the UI automatic Z-ordering. * Hide the UI when the owner is minimized. */ if (hFocusImc != NULL_HIMC) { PINPUTCONTEXT pInputContext; /* * Get the window who's given the context. */ if ((pInputContext = fpImmLockIMC(hFocusImc)) != NULL) { //UserAssert(hwndFocus == pInputContext->hWnd); if (hwndFocus != pInputContext->hWnd) { fpImmUnlockIMC(hFocusImc); /* * Pq->spwndFocus has been changed so far... * All we can do is just to bail out. */ return 0L; } } else return 0L; // the context was broken if ((pInputContext->fdw31Compat & F31COMPAT_ECSETCFS) && hwndPrevIMC != hwndFocus) { COMPOSITIONFORM cf; /* * Set CFS_DEFAULT.... */ RtlZeroMemory(&cf, sizeof(cf)); fpImmSetCompositionWindow(hFocusImc, &cf); pInputContext->fdw31Compat &= ~F31COMPAT_ECSETCFS; } fpImmUnlockIMC(hFocusImc); if (NtUserSetImeOwnerWindow(HW(pimeui->spwnd), hwndFocus)) pimeui->hwndIMC = hwndFocus; } else { /* * NULL IMC is getting activated */ pimeui->hwndIMC = hwndFocus; NtUserSetImeOwnerWindow(HW(pimeui->spwnd), NULL); } } lRet = SendMessageToUI(pimeui, message, wParam, lParam, FALSE); if (pimeui->spwnd == NULL) { // Unusual case in stress.. // IME window has been destroyed during the callback RIPMSG0(RIP_WARNING, "ImmSetContextHandler: pimeui->spwnd is NULL after SendMessageToUI."); return 0L; } if (pimeui->fCtrlShowStatus && GetIMEShowStatus()) { PWND pwndFocus, pwndIMC, pwndPrevIMC; HWND hwndActive; hwndFocus = NtUserQueryWindow(HWq(pimeui->spwnd), WindowFocusWindow); pwndFocus = ValidateHwndNoRip(hwndFocus); if ((BOOL)wParam == TRUE) { HWND hwndIme; /* * BOGUS BOGUS * The following if statement is still insufficient * it needs to think what WM_IME_SETCONTEXT:TRUE should do * in the case of WINNLSEnableIME(true) - ref.win95c B#8548. */ UserAssert(pimeui->spwnd); if (pwndFocus != NULL && GETPTI(pimeui->spwnd) == GETPTI(pwndFocus)) { if (!pimeui->fShowStatus) { /* * We have never sent IMN_OPENSTATUSWINDOW yet.... */ if (ValidateHwndNoRip(KHWND_TO_HWND(pimeui->hwndIMC))) { SendOpenStatusNotify(pimeui, KHWND_TO_HWND(pimeui->hwndIMC), TRUE); } } else if ((pwndIMC = ValidateHwndNoRip(KHWND_TO_HWND(pimeui->hwndIMC))) != NULL && (pwndPrevIMC = ValidateHwndNoRip(hwndPrevIMC)) != NULL && GetTopLevelWindow(pwndIMC) != GetTopLevelWindow(pwndPrevIMC)) { /* * Because the top level window of IME Wnd was changed. */ SendOpenStatusNotify(pimeui, hwndPrevIMC, FALSE); SendOpenStatusNotify(pimeui, KHWND_TO_HWND(pimeui->hwndIMC), TRUE); } } /* * There may have other IME windows that have fShowStatus. * We need to check the fShowStatus in the window list. */ hwndIme = HW(pimeui->spwnd); if (hwndIme) { NtUserCheckImeShowStatusInThread(hwndIme); } } else { /* * When focus was removed from this thread, we close the * status window. * Because focus was already removed from whndPrevIMC, * hwndPrevIMC may be destroyed but we need to close the * status window. */ hwndActive = NtUserQueryWindow(HW(pimeui->spwnd), WindowActiveWindow); UserAssert(pimeui->spwnd); if (pwndFocus == NULL || GETPTI(pimeui->spwnd) != GETPTI(pwndFocus) || hwndActive == NULL) { if (IsWindow(hwndPrevIMC)) { RIPMSG1(RIP_VERBOSE, "ImeSetContextHandler: notifying OpenStatus (FALSE) to hwnd=%p", hwndPrevIMC); SendOpenStatusNotify(pimeui, hwndPrevIMC, FALSE); } else { RIPMSG1(RIP_VERBOSE, "ImeSetContextHandler: sending IMN_CLOSESTATUSWINDOW to UIwnd=%p", pimeui->hwndUI); pimeui->fShowStatus = FALSE; SendMessageToUI(pimeui, WM_IME_NOTIFY, IMN_CLOSESTATUSWINDOW, 0L, FALSE); } } } } return lRet; } LRESULT ImeNotifyHandler( PIMEUI pimeui, UINT message, WPARAM wParam, LPARAM lParam) { HWND hwndUI; LRESULT lRet = 0L; HIMC hImc; PINPUTCONTEXT pInputContext; switch (wParam) { case IMN_PRIVATE: hwndUI = GETUI(pimeui); if (IsWindow(hwndUI)) lRet = SendMessage(hwndUI, message, wParam, lParam); break; case IMN_SETCONVERSIONMODE: case IMN_SETOPENSTATUS: // // notify shell and keyboard the conversion mode change // // if this message is sent from ImmSetOpenStatus or // ImmSetConversionStatus, we have already notified // kernel the change. This is a little bit redundant. // // if application has eaten the message, we won't be here. // We need to think about the possibility later. // hImc = GETIMC(pimeui); if ((pInputContext = fpImmLockIMC(hImc)) != NULL) { if ( IsWindow(KHWND_TO_HWND(pimeui->hwndIMC)) ) { NtUserNotifyIMEStatus( KHWND_TO_HWND(pimeui->hwndIMC), (DWORD)pInputContext->fOpen, pInputContext->fdwConversion ); } else if (gfConIme == TRUE) { /* * Special handling for Console IME is needed */ if (pimeui->spwnd) { // If IME window is still there. PWND pwndOwner = REBASEPWND(pimeui->spwnd, spwndOwner); if (pwndOwner != NULL) { NtUserNotifyIMEStatus(HWq(pwndOwner), (DWORD)pInputContext->fOpen, pInputContext->fdwConversion); } } } fpImmUnlockIMC(hImc); } /*** FALL THROUGH ***/ default: TAGMSG4(DBGTAG_IMM, "ImeNotifyHandler: sending to pimeui->ui=%p, msg=%x, wParam=%x, lParam=%x\n", GETUI(pimeui), message, wParam, lParam); lRet = SendMessageToUI(pimeui, message, wParam, lParam, FALSE); } return lRet; } HWND CreateIMEUI( PIMEUI pimeui, HKL hKL) { PWND pwndIme = pimeui->spwnd; HWND hwndUI; IMEINFOEX iiex; PIMEDPI pimedpi; WNDCLASS wndcls; if (!fpImmGetImeInfoEx(&iiex, ImeInfoExKeyboardLayout, &hKL)) return (HWND)NULL; if ((pimedpi = fpImmLockImeDpi(hKL)) == NULL) { RIPMSG1(RIP_WARNING, "CreateIMEUI: ImmLockImeDpi(%lx) failed.", hKL); return (HWND)NULL; } if (!GetClassInfoW(pimedpi->hInst, iiex.wszUIClass, &wndcls)) { RIPMSG1(RIP_WARNING, "CreateIMEUI: GetClassInfoW(%ws) failed\n", iiex.wszUIClass); fpImmUnlockImeDpi(pimedpi); return (HWND)NULL; } // HACK HACK HACK if ((wndcls.style & CS_IME) == 0) { RIPMSG1(RIP_ERROR, "CreateIMEUI: the Window Class (%S) does not have CS_IME flag on !!!\n", wndcls.lpszClassName); } if (iiex.ImeInfo.fdwProperty & IME_PROP_UNICODE) { /* * For Unicode IME, we create an Unicode IME UI window. */ hwndUI = CreateWindowExW(0L, iiex.wszUIClass, iiex.wszUIClass, WS_POPUP|WS_DISABLED, 0, 0, 0, 0, HWq(pwndIme), 0, wndcls.hInstance, NULL); } else { /* * For ANSI IME, we create an ANSI IME UI window. */ LPSTR pszClass; int i; i = WCSToMB(iiex.wszUIClass, -1, &pszClass, -1, TRUE); if (i == 0) { RIPMSG1(RIP_WARNING, "CreateIMEUI: failed in W->A conversion (%S)", iiex.wszUIClass); fpImmUnlockImeDpi(pimedpi); return (HWND)NULL; } pszClass[i] = '\0'; hwndUI = CreateWindowExA(0L, pszClass, pszClass, WS_POPUP|WS_DISABLED, 0, 0, 0, 0, HWq(pwndIme), 0, wndcls.hInstance, NULL); UserLocalFree(pszClass); } if (hwndUI) NtUserSetWindowLongPtr(hwndUI, IMMGWLP_IMC, (LONG_PTR)GETIMC(pimeui), FALSE); fpImmUnlockImeDpi(pimedpi); return hwndUI; } VOID DestroyIMEUI( PIMEUI pimeui) { // This has currently nothing to do except for destroying the UI. // Review: Need to notify the UI with WM_IME_SETCONTEXT ? // Review: This doesn't support Multiple IME install yet. HWND hwndUI = GETUI(pimeui); if (IsWindow(hwndUI)) { pimeui->fDestroy = TRUE; /* * We need this verify because the owner might have already * killed it during its termination. */ NtUserDestroyWindow(hwndUI); } pimeui->fDestroy = FALSE; /* * Reinitialize show status of the IME status window so that * notification message will be sent when needed. */ pimeui->fShowStatus = FALSE; SETUI(pimeui, NULL); return; } /***************************************************************************\ * SendMessageToUI * * History: * 09-Apr-1996 wkwok Created \***************************************************************************/ LRESULT SendMessageToUI( PIMEUI pimeui, UINT message, WPARAM wParam, LPARAM lParam, BOOL fAnsi) { PWND pwndUI; LRESULT lRet; TAGMSG1(DBGTAG_IMM, "Sending to UI msg[%04X]\n", message); pwndUI = ValidateHwndNoRip(GETUI(pimeui)); if (pwndUI == NULL || pimeui->spwnd == NULL) return 0L; if (TestWF(pimeui->spwnd, WFINDESTROY) || TestWF(pimeui->spwnd, WFDESTROYED) || TestWF(pwndUI, WFINDESTROY) || TestWF(pwndUI, WFDESTROYED)) { return 0L; } InterlockedIncrement(&pimeui->nCntInIMEProc); // Mark to avoid recursion. lRet = SendMessageWorker(pwndUI, message, wParam, lParam, fAnsi); InterlockedDecrement(&pimeui->nCntInIMEProc); // Mark to avoid recursion. return lRet; } /***************************************************************************\ * SendOpenStatusNotify * * History: * 09-Apr-1996 wkwok Created \***************************************************************************/ VOID SendOpenStatusNotify( PIMEUI pimeui, HWND hwndApp, BOOL fOpen) { WPARAM wParam = fOpen ? IMN_OPENSTATUSWINDOW : IMN_CLOSESTATUSWINDOW; pimeui->fShowStatus = fOpen; if (Is400Compat(GetClientInfo()->dwExpWinVer)) { TAGMSG2(DBGTAG_IMM, "SendOpenStatusNotify: sending to hwnd=%lx, wParam=%d\n", hwndApp, wParam); SendMessage(hwndApp, WM_IME_NOTIFY, wParam, 0L); } else { TAGMSG2(DBGTAG_IMM, "SendOpenStatusNotify:sending to imeui->UI=%p, wParam=%d\n", GETUI(pimeui), wParam); SendMessageToUI(pimeui, WM_IME_NOTIFY, wParam, 0L, FALSE); } return; } VOID ImeSetImc( PIMEUI pimeui, HIMC hImc) { HWND hImeWnd = HW(pimeui->spwnd); HIMC hOldImc = GETIMC(pimeui); /* * return if nothing to change. */ if (hImc == hOldImc) return; /* * Unmark the old input context. */ if (hOldImc != NULL_HIMC) ImeMarkUsedContext(NULL, hOldImc); /* * Update the in use input context for this IME window. */ pimeui->hIMC = hImc; /* * Mark the new input context. */ if (hImc != NULL_HIMC) ImeMarkUsedContext(hImeWnd, hImc); } /***************************************************************************\ * FocusSetIMCContext() * * History: * 21-Mar-1996 wkwok Created \***************************************************************************/ VOID FocusSetIMCContext( HWND hWnd, BOOL fActivate) { HIMC hImc; if (IsWindow(hWnd)) { hImc = fpImmGetContext(hWnd); fpImmSetActiveContext(hWnd, hImc, fActivate); fpImmReleaseContext(hWnd, hImc); } else { fpImmSetActiveContext(NULL, NULL_HIMC, fActivate); } return; } BOOL ImeBroadCastMsg( PIMEUI pimeui, UINT message, WPARAM wParam, LPARAM lParam) { UNREFERENCED_PARAMETER(pimeui); UNREFERENCED_PARAMETER(message); UNREFERENCED_PARAMETER(wParam); UNREFERENCED_PARAMETER(lParam); return TRUE; } /***************************************************************************\ * ImeMarkUsedContext() * * Some IME windows can not share the same input context. This function * marks the specified hImc to be in used by the specified IME window. * * History: * 12-Mar-1996 wkwok Created \***************************************************************************/ VOID ImeMarkUsedContext( HWND hImeWnd, HIMC hImc) { PIMC pImc; pImc = HMValidateHandle((HANDLE)hImc, TYPE_INPUTCONTEXT); if (pImc == NULL) { RIPMSG1(RIP_WARNING, "ImeMarkUsedContext: Invalid hImc (=%lx).", hImc); return; } UserAssert( ValidateHwndNoRip(pImc->hImeWnd) == NULL || hImeWnd == NULL ); /* * Nothing to change? */ if (pImc->hImeWnd == hImeWnd) return; NtUserUpdateInputContext(hImc, UpdateInUseImeWindow, (ULONG_PTR)hImeWnd); return; } /***************************************************************************\ * ImeIsUsableContext() * * Some IME windows can not share the same input context. This function * checks whether the specified hImc can be used (means 'Set activated') * by the specified IME window. * * Return: TRUE - OK to use the hImc by hImeWnd. * FALSE - otherwise. * * History: * 12-Mar-1996 wkwok Created \***************************************************************************/ BOOL ImeIsUsableContext( HWND hImeWnd, HIMC hImc) { PIMC pImc; UserAssert(hImeWnd != NULL); pImc = HMValidateHandle((HANDLE)hImc, TYPE_INPUTCONTEXT); if (pImc == NULL) { RIPMSG1(RIP_WARNING, "ImeIsUsableContext: Invalid hImc (=%lx).", hImc); return FALSE; } if ( pImc->hImeWnd == NULL || pImc->hImeWnd == hImeWnd || ValidateHwndNoRip(pImc->hImeWnd) == NULL ) { return TRUE; } return FALSE; } /***************************************************************************\ * GetIMEShowStatus() * * Get the global IME show status from kernel. * * History: * 19-Sep-1996 takaok Ported from internat.exe. \***************************************************************************/ BOOL GetIMEShowStatus(void) { return (BOOL)NtUserCallNoParam(SFI__GETIMESHOWSTATUS); } /***************************************************************************\ * IMEIndicatorGetMenuIDData * * History: * 3-Nov-97 Hiroyama \***************************************************************************/ BOOL IMEIndicatorGetMenuIDData(PUINT puMenuID, PDWORD pdwData) { HANDLE hinstIndic; WCHAR szModule[MAX_PATH + 1]; if (! GetSystemModulePath(SYSTEM_DIR, szModule, ARRAYSIZE(szModule), szIndicDLL)) { gpfnGetIMEMenuItemData = NULL; return FALSE; } hinstIndic = GetModuleHandle(szModule); if (hinstIndic == NULL) { gpfnGetIMEMenuItemData = NULL; return FALSE; } if (!gpfnGetIMEMenuItemData) { gpfnGetIMEMenuItemData = GetProcAddress(hinstIndic, (LPSTR)ORD_GETIMEMENUITEMDATA); } if (!gpfnGetIMEMenuItemData) return FALSE; (*(FPGETIMEMENUITEMDATA)gpfnGetIMEMenuItemData)(puMenuID, pdwData); return TRUE; } BOOL SyncSoftKbdState( HIMC hImc, LPARAM lParam) { DWORD fdwConversion, fdwSentence, fdwNewConversion; fpImmGetConversionStatus(hImc, &fdwConversion, &fdwSentence); if (lParam) { fdwNewConversion = fdwConversion | IME_CMODE_SOFTKBD; } else { fdwNewConversion = fdwConversion & ~IME_CMODE_SOFTKBD; } if (fdwNewConversion != fdwConversion) { fpImmSetConversionStatus(hImc, fdwNewConversion, fdwSentence); } return TRUE; }