First commit.
This commit is contained in:
commit
7b83802bb2
103 files changed
+19016
No files matched your search
@@ -0,0 +1,457 @@
|
||||
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
||||
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
||||
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
||||
// PARTICULAR PURPOSE.
|
||||
//
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved
|
||||
|
||||
#include "Private.h"
|
||||
#include "Globals.h"
|
||||
#include "SampleIME.h"
|
||||
#include "CandidateListUIPresenter.h"
|
||||
#include "CompositionProcessorEngine.h"
|
||||
#include "KeyHandlerEditSession.h"
|
||||
#include "Compartment.h"
|
||||
|
||||
// 0xF003, 0xF004 are the keys that the touch keyboard sends for next/previous
|
||||
#define THIRDPARTY_NEXTPAGE static_cast<WORD>(0xF003)
|
||||
#define THIRDPARTY_PREVPAGE static_cast<WORD>(0xF004)
|
||||
|
||||
// Because the code mostly works with VKeys, here map a WCHAR back to a VKKey for certain
|
||||
// vkeys that the IME handles specially
|
||||
__inline UINT VKeyFromVKPacketAndWchar(UINT vk, WCHAR wch)
|
||||
{
|
||||
UINT vkRet = vk;
|
||||
if (LOWORD(vk) == VK_PACKET)
|
||||
{
|
||||
if (wch == L' ')
|
||||
{
|
||||
vkRet = VK_SPACE;
|
||||
}
|
||||
else if ((wch >= L'0') && (wch <= L'9'))
|
||||
{
|
||||
vkRet = static_cast<UINT>(wch);
|
||||
}
|
||||
else if ((wch >= L'a') && (wch <= L'z'))
|
||||
{
|
||||
vkRet = (UINT)(L'A') + ((UINT)(L'z') - static_cast<UINT>(wch));
|
||||
}
|
||||
else if ((wch >= L'A') && (wch <= L'Z'))
|
||||
{
|
||||
vkRet = static_cast<UINT>(wch);
|
||||
}
|
||||
else if (wch == THIRDPARTY_NEXTPAGE)
|
||||
{
|
||||
vkRet = VK_NEXT;
|
||||
}
|
||||
else if (wch == THIRDPARTY_PREVPAGE)
|
||||
{
|
||||
vkRet = VK_PRIOR;
|
||||
}
|
||||
}
|
||||
return vkRet;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
// _IsKeyEaten
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
BOOL CSampleIME::_IsKeyEaten(_In_ ITfContext *pContext, UINT codeIn, _Out_ UINT *pCodeOut, _Out_writes_(1) WCHAR *pwch, _Out_opt_ _KEYSTROKE_STATE *pKeyState)
|
||||
{
|
||||
pContext;
|
||||
|
||||
*pCodeOut = codeIn;
|
||||
|
||||
BOOL isOpen = FALSE;
|
||||
CCompartment CompartmentKeyboardOpen(_pThreadMgr, _tfClientId, GUID_COMPARTMENT_KEYBOARD_OPENCLOSE);
|
||||
CompartmentKeyboardOpen._GetCompartmentBOOL(isOpen);
|
||||
|
||||
BOOL isDoubleSingleByte = FALSE;
|
||||
CCompartment CompartmentDoubleSingleByte(_pThreadMgr, _tfClientId, Global::SampleIMEGuidCompartmentDoubleSingleByte);
|
||||
CompartmentDoubleSingleByte._GetCompartmentBOOL(isDoubleSingleByte);
|
||||
|
||||
BOOL isPunctuation = FALSE;
|
||||
CCompartment CompartmentPunctuation(_pThreadMgr, _tfClientId, Global::SampleIMEGuidCompartmentPunctuation);
|
||||
CompartmentPunctuation._GetCompartmentBOOL(isPunctuation);
|
||||
|
||||
if (pKeyState)
|
||||
{
|
||||
pKeyState->Category = CATEGORY_NONE;
|
||||
pKeyState->Function = FUNCTION_NONE;
|
||||
}
|
||||
if (pwch)
|
||||
{
|
||||
*pwch = L'\0';
|
||||
}
|
||||
|
||||
// if the keyboard is disabled, we don't eat keys.
|
||||
if (_IsKeyboardDisabled())
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
//
|
||||
// Map virtual key to character code
|
||||
//
|
||||
BOOL isTouchKeyboardSpecialKeys = FALSE;
|
||||
WCHAR wch = ConvertVKey(codeIn);
|
||||
*pCodeOut = VKeyFromVKPacketAndWchar(codeIn, wch);
|
||||
if ((wch == THIRDPARTY_NEXTPAGE) || (wch == THIRDPARTY_PREVPAGE))
|
||||
{
|
||||
// We always eat the above softkeyboard special keys
|
||||
isTouchKeyboardSpecialKeys = TRUE;
|
||||
if (pwch)
|
||||
{
|
||||
*pwch = wch;
|
||||
}
|
||||
}
|
||||
|
||||
// if the keyboard is closed, we don't eat keys, with the exception of the touch keyboard specials keys
|
||||
if (!isOpen && !isDoubleSingleByte && !isPunctuation)
|
||||
{
|
||||
return isTouchKeyboardSpecialKeys;
|
||||
}
|
||||
|
||||
if (pwch)
|
||||
{
|
||||
*pwch = wch;
|
||||
}
|
||||
|
||||
//
|
||||
// Get composition engine
|
||||
//
|
||||
CCompositionProcessorEngine *pCompositionProcessorEngine;
|
||||
pCompositionProcessorEngine = _pCompositionProcessorEngine;
|
||||
|
||||
if (isOpen)
|
||||
{
|
||||
//
|
||||
// The candidate or phrase list handles the keys through ITfKeyEventSink.
|
||||
//
|
||||
// eat only keys that CKeyHandlerEditSession can handles.
|
||||
//
|
||||
if (pCompositionProcessorEngine->IsVirtualKeyNeed(*pCodeOut, pwch, _IsComposing(), _candidateMode, _isCandidateWithWildcard, pKeyState))
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Punctuation
|
||||
//
|
||||
if (pCompositionProcessorEngine->IsPunctuation(wch))
|
||||
{
|
||||
if ((_candidateMode == CANDIDATE_NONE) && isPunctuation)
|
||||
{
|
||||
if (pKeyState)
|
||||
{
|
||||
pKeyState->Category = CATEGORY_COMPOSING;
|
||||
pKeyState->Function = FUNCTION_PUNCTUATION;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Double/Single byte
|
||||
//
|
||||
if (isDoubleSingleByte && pCompositionProcessorEngine->IsDoubleSingleByte(wch))
|
||||
{
|
||||
if (_candidateMode == CANDIDATE_NONE)
|
||||
{
|
||||
if (pKeyState)
|
||||
{
|
||||
pKeyState->Category = CATEGORY_COMPOSING;
|
||||
pKeyState->Function = FUNCTION_DOUBLE_SINGLE_BYTE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
return isTouchKeyboardSpecialKeys;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
// ConvertVKey
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
WCHAR CSampleIME::ConvertVKey(UINT code)
|
||||
{
|
||||
//
|
||||
// Map virtual key to scan code
|
||||
//
|
||||
UINT scanCode = 0;
|
||||
scanCode = MapVirtualKey(code, 0);
|
||||
|
||||
//
|
||||
// Keyboard state
|
||||
//
|
||||
BYTE abKbdState[256] = {'\0'};
|
||||
if (!GetKeyboardState(abKbdState))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// Map virtual key to character code
|
||||
//
|
||||
WCHAR wch = '\0';
|
||||
if (ToUnicode(code, scanCode, abKbdState, &wch, 1, 0) == 1)
|
||||
{
|
||||
return wch;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
// _IsKeyboardDisabled
|
||||
//
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
BOOL CSampleIME::_IsKeyboardDisabled()
|
||||
{
|
||||
ITfDocumentMgr* pDocMgrFocus = nullptr;
|
||||
ITfContext* pContext = nullptr;
|
||||
BOOL isDisabled = FALSE;
|
||||
|
||||
if ((_pThreadMgr->GetFocus(&pDocMgrFocus) != S_OK) ||
|
||||
(pDocMgrFocus == nullptr))
|
||||
{
|
||||
// if there is no focus document manager object, the keyboard
|
||||
// is disabled.
|
||||
isDisabled = TRUE;
|
||||
}
|
||||
else if ((pDocMgrFocus->GetTop(&pContext) != S_OK) ||
|
||||
(pContext == nullptr))
|
||||
{
|
||||
// if there is no context object, the keyboard is disabled.
|
||||
isDisabled = TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
CCompartment CompartmentKeyboardDisabled(_pThreadMgr, _tfClientId, GUID_COMPARTMENT_KEYBOARD_DISABLED);
|
||||
CompartmentKeyboardDisabled._GetCompartmentBOOL(isDisabled);
|
||||
|
||||
CCompartment CompartmentEmptyContext(_pThreadMgr, _tfClientId, GUID_COMPARTMENT_EMPTYCONTEXT);
|
||||
CompartmentEmptyContext._GetCompartmentBOOL(isDisabled);
|
||||
}
|
||||
|
||||
if (pContext)
|
||||
{
|
||||
pContext->Release();
|
||||
}
|
||||
|
||||
if (pDocMgrFocus)
|
||||
{
|
||||
pDocMgrFocus->Release();
|
||||
}
|
||||
|
||||
return isDisabled;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
// ITfKeyEventSink::OnSetFocus
|
||||
//
|
||||
// Called by the system whenever this service gets the keystroke device focus.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
STDAPI CSampleIME::OnSetFocus(BOOL fForeground)
|
||||
{
|
||||
fForeground;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
// ITfKeyEventSink::OnTestKeyDown
|
||||
//
|
||||
// Called by the system to query this service wants a potential keystroke.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
STDAPI CSampleIME::OnTestKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten)
|
||||
{
|
||||
Global::UpdateModifiers(wParam, lParam);
|
||||
|
||||
_KEYSTROKE_STATE KeystrokeState;
|
||||
WCHAR wch = '\0';
|
||||
UINT code = 0;
|
||||
*pIsEaten = _IsKeyEaten(pContext, (UINT)wParam, &code, &wch, &KeystrokeState);
|
||||
|
||||
if (KeystrokeState.Category == CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION)
|
||||
{
|
||||
//
|
||||
// Invoke key handler edit session
|
||||
//
|
||||
KeystrokeState.Category = CATEGORY_COMPOSING;
|
||||
|
||||
_InvokeKeyHandler(pContext, code, wch, (DWORD)lParam, KeystrokeState);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
// ITfKeyEventSink::OnKeyDown
|
||||
//
|
||||
// Called by the system to offer this service a keystroke. If *pIsEaten == TRUE
|
||||
// on exit, the application will not handle the keystroke.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
STDAPI CSampleIME::OnKeyDown(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten)
|
||||
{
|
||||
Global::UpdateModifiers(wParam, lParam);
|
||||
|
||||
_KEYSTROKE_STATE KeystrokeState;
|
||||
WCHAR wch = '\0';
|
||||
UINT code = 0;
|
||||
|
||||
*pIsEaten = _IsKeyEaten(pContext, (UINT)wParam, &code, &wch, &KeystrokeState);
|
||||
|
||||
if (*pIsEaten)
|
||||
{
|
||||
bool needInvokeKeyHandler = true;
|
||||
//
|
||||
// Invoke key handler edit session
|
||||
//
|
||||
if (code == VK_ESCAPE)
|
||||
{
|
||||
KeystrokeState.Category = CATEGORY_COMPOSING;
|
||||
}
|
||||
|
||||
// Always eat THIRDPARTY_NEXTPAGE and THIRDPARTY_PREVPAGE keys, but don't always process them.
|
||||
if ((wch == THIRDPARTY_NEXTPAGE) || (wch == THIRDPARTY_PREVPAGE))
|
||||
{
|
||||
needInvokeKeyHandler = !((KeystrokeState.Category == CATEGORY_NONE) && (KeystrokeState.Function == FUNCTION_NONE));
|
||||
}
|
||||
|
||||
if (needInvokeKeyHandler)
|
||||
{
|
||||
_InvokeKeyHandler(pContext, code, wch, (DWORD)lParam, KeystrokeState);
|
||||
}
|
||||
}
|
||||
else if (KeystrokeState.Category == CATEGORY_INVOKE_COMPOSITION_EDIT_SESSION)
|
||||
{
|
||||
// Invoke key handler edit session
|
||||
KeystrokeState.Category = CATEGORY_COMPOSING;
|
||||
_InvokeKeyHandler(pContext, code, wch, (DWORD)lParam, KeystrokeState);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
// ITfKeyEventSink::OnTestKeyUp
|
||||
//
|
||||
// Called by the system to query this service wants a potential keystroke.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
STDAPI CSampleIME::OnTestKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten)
|
||||
{
|
||||
if (pIsEaten == nullptr)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
Global::UpdateModifiers(wParam, lParam);
|
||||
|
||||
WCHAR wch = '\0';
|
||||
UINT code = 0;
|
||||
|
||||
*pIsEaten = _IsKeyEaten(pContext, (UINT)wParam, &code, &wch, NULL);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
// ITfKeyEventSink::OnKeyUp
|
||||
//
|
||||
// Called by the system to offer this service a keystroke. If *pIsEaten == TRUE
|
||||
// on exit, the application will not handle the keystroke.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
STDAPI CSampleIME::OnKeyUp(ITfContext *pContext, WPARAM wParam, LPARAM lParam, BOOL *pIsEaten)
|
||||
{
|
||||
Global::UpdateModifiers(wParam, lParam);
|
||||
|
||||
WCHAR wch = '\0';
|
||||
UINT code = 0;
|
||||
|
||||
*pIsEaten = _IsKeyEaten(pContext, (UINT)wParam, &code, &wch, NULL);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
// ITfKeyEventSink::OnPreservedKey
|
||||
//
|
||||
// Called when a hotkey (registered by us, or by the system) is typed.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
STDAPI CSampleIME::OnPreservedKey(ITfContext *pContext, REFGUID rguid, BOOL *pIsEaten)
|
||||
{
|
||||
pContext;
|
||||
|
||||
CCompositionProcessorEngine *pCompositionProcessorEngine;
|
||||
pCompositionProcessorEngine = _pCompositionProcessorEngine;
|
||||
|
||||
pCompositionProcessorEngine->OnPreservedKey(rguid, pIsEaten, _GetThreadMgr(), _GetClientId());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
// _InitKeyEventSink
|
||||
//
|
||||
// Advise a keystroke sink.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
BOOL CSampleIME::_InitKeyEventSink()
|
||||
{
|
||||
ITfKeystrokeMgr* pKeystrokeMgr = nullptr;
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
if (FAILED(_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr)))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hr = pKeystrokeMgr->AdviseKeyEventSink(_tfClientId, (ITfKeyEventSink *)this, TRUE);
|
||||
|
||||
pKeystrokeMgr->Release();
|
||||
|
||||
return (hr == S_OK);
|
||||
}
|
||||
|
||||
//+---------------------------------------------------------------------------
|
||||
//
|
||||
// _UninitKeyEventSink
|
||||
//
|
||||
// Unadvise a keystroke sink. Assumes we have advised one already.
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
void CSampleIME::_UninitKeyEventSink()
|
||||
{
|
||||
ITfKeystrokeMgr* pKeystrokeMgr = nullptr;
|
||||
|
||||
if (FAILED(_pThreadMgr->QueryInterface(IID_ITfKeystrokeMgr, (void **)&pKeystrokeMgr)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pKeystrokeMgr->UnadviseKeyEventSink(_tfClientId);
|
||||
|
||||
pKeystrokeMgr->Release();
|
||||
}
|
||||
Reference in new issue
Block a user