📄 imeui.cpp
字号:
//--------------------------------------------------------------------------------------
// File: ImeUi.cpp
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------
#include "dxut.h"
#include "ImeUi.h"
#include <math.h>
#include <msctf.h>
#include <malloc.h>
#include <strsafe.h>
// Ignore typecast warnings
#pragma warning( disable : 4312 )
#pragma warning( disable : 4244 )
#pragma warning( disable : 4311 )
#define MAX_CANDIDATE_LENGTH 256
#define COUNTOF(a) ( sizeof( a ) / sizeof( ( a )[0] ) )
#define POSITION_UNINITIALIZED ((DWORD)-1)
#define LANG_CHT MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL)
#define LANG_CHS MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)
#define MAKEIMEVERSION(major,minor) ( (DWORD)( ( (BYTE)( major ) << 24 ) | ( (BYTE)( minor ) << 16 ) ) )
#define IMEID_VER(dwId) ( ( dwId ) & 0xffff0000 )
#define IMEID_LANG(dwId) ( ( dwId ) & 0x0000ffff )
#define _CHT_HKL_DAYI ( (HKL)0xE0060404 ) // DaYi
#define _CHT_HKL_NEW_PHONETIC ( (HKL)0xE0080404 ) // New Phonetic
#define _CHT_HKL_NEW_CHANG_JIE ( (HKL)0xE0090404 ) // New Chang Jie
#define _CHT_HKL_NEW_QUICK ( (HKL)0xE00A0404 ) // New Quick
#define _CHT_HKL_HK_CANTONESE ( (HKL)0xE00B0404 ) // Hong Kong Cantonese
#define _CHT_IMEFILENAME "TINTLGNT.IME" // New Phonetic
#define _CHT_IMEFILENAME2 "CINTLGNT.IME" // New Chang Jie
#define _CHT_IMEFILENAME3 "MSTCIPHA.IME" // Phonetic 5.1
#define IMEID_CHT_VER42 ( LANG_CHT | MAKEIMEVERSION( 4, 2 ) ) // New(Phonetic/ChanJie)IME98 : 4.2.x.x // Win98
#define IMEID_CHT_VER43 ( LANG_CHT | MAKEIMEVERSION( 4, 3 ) ) // New(Phonetic/ChanJie)IME98a : 4.3.x.x // Win2k
#define IMEID_CHT_VER44 ( LANG_CHT | MAKEIMEVERSION( 4, 4 ) ) // New ChanJie IME98b : 4.4.x.x // WinXP
#define IMEID_CHT_VER50 ( LANG_CHT | MAKEIMEVERSION( 5, 0 ) ) // New(Phonetic/ChanJie)IME5.0 : 5.0.x.x // WinME
#define IMEID_CHT_VER51 ( LANG_CHT | MAKEIMEVERSION( 5, 1 ) ) // New(Phonetic/ChanJie)IME5.1 : 5.1.x.x // IME2002(w/OfficeXP)
#define IMEID_CHT_VER52 ( LANG_CHT | MAKEIMEVERSION( 5, 2 ) ) // New(Phonetic/ChanJie)IME5.2 : 5.2.x.x // IME2002a(w/WinXP)
#define IMEID_CHT_VER60 ( LANG_CHT | MAKEIMEVERSION( 6, 0 ) ) // New(Phonetic/ChanJie)IME6.0 : 6.0.x.x // New IME 6.0(web download)
#define IMEID_CHT_VER_VISTA ( LANG_CHT | MAKEIMEVERSION( 7, 0 ) ) // All TSF TIP under Cicero UI-less mode: a hack to make GetImeId() return non-zero value
#define _CHS_HKL ( (HKL)0xE00E0804 ) // MSPY
#define _CHS_IMEFILENAME "PINTLGNT.IME" // MSPY1.5/2/3
#define _CHS_IMEFILENAME2 "MSSCIPYA.IME" // MSPY3 for OfficeXP
#define IMEID_CHS_VER41 ( LANG_CHS | MAKEIMEVERSION( 4, 1 ) ) // MSPY1.5 // SCIME97 or MSPY1.5 (w/Win98, Office97)
#define IMEID_CHS_VER42 ( LANG_CHS | MAKEIMEVERSION( 4, 2 ) ) // MSPY2 // Win2k/WinME
#define IMEID_CHS_VER53 ( LANG_CHS | MAKEIMEVERSION( 5, 3 ) ) // MSPY3 // WinXP
static CHAR signature[] = "%%%IMEUILIB:070111%%%";
static IMEUI_APPEARANCE gSkinIME = {
0, // symbolColor;
0x404040, // symbolColorOff;
0xff000000, // symbolColorText;
24, // symbolHeight;
0xa0, // symbolTranslucence;
0, // symbolPlacement;
NULL, // symbolFont;
0xffffffff, // candColorBase;
0xff000000, // candColorBorder;
0, // candColorText;
0x00ffff00, // compColorInput;
0x000000ff, // compColorTargetConv;
0x0000ff00, // compColorConverted;
0x00ff0000, // compColorTargetNotConv;
0x00ff0000, // compColorInputErr;
0x80, // compTranslucence;
0, // compColorText;
2, // caretWidth;
1, // caretYMargin;
};
struct _SkinCompStr
{
DWORD colorInput;
DWORD colorTargetConv;
DWORD colorConverted;
DWORD colorTargetNotConv;
DWORD colorInputErr;
};
_SkinCompStr gSkinCompStr;
// Definition from Win98DDK version of IMM.H
typedef struct tagINPUTCONTEXT2 {
HWND hWnd;
BOOL fOpen;
POINT ptStatusWndPos;
POINT ptSoftKbdPos;
DWORD fdwConversion;
DWORD fdwSentence;
union {
LOGFONTA A;
LOGFONTW W;
} lfFont;
COMPOSITIONFORM cfCompForm;
CANDIDATEFORM cfCandForm[4];
HIMCC hCompStr;
HIMCC hCandInfo;
HIMCC hGuideLine;
HIMCC hPrivate;
DWORD dwNumMsgBuf;
HIMCC hMsgBuf;
DWORD fdwInit;
DWORD dwReserve[3];
} INPUTCONTEXT2, *PINPUTCONTEXT2, NEAR *NPINPUTCONTEXT2, FAR *LPINPUTCONTEXT2;
// Class to disable Cicero in case ImmDisableTextFrameService() doesn't disable it completely
class CDisableCicero
{
public:
CDisableCicero() : m_ptim( NULL ), m_bComInit( false )
{
}
~CDisableCicero()
{
Uninitialize();
}
void Initialize()
{
if ( m_bComInit )
{
return;
}
HRESULT hr;
hr = CoInitializeEx( NULL, COINIT_APARTMENTTHREADED );
if ( SUCCEEDED( hr ) )
{
m_bComInit = true;
hr = CoCreateInstance( CLSID_TF_ThreadMgr,
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(ITfThreadMgr),
(void**)&m_ptim );
}
}
void Uninitialize()
{
if ( m_ptim )
{
m_ptim->Release();
m_ptim = NULL;
}
if ( m_bComInit )
CoUninitialize();
m_bComInit = false;
}
void DisableCiceroOnThisWnd( HWND hwnd )
{
if ( m_ptim == NULL )
return;
ITfDocumentMgr* pdimPrev; // the dim that is associated previously.
// Associate NULL dim to the window.
// When this window gets the focus, Cicero does not work and IMM32 IME
// will be activated.
if ( SUCCEEDED( m_ptim->AssociateFocus( hwnd, NULL, &pdimPrev ) ) )
{
if ( pdimPrev )
pdimPrev->Release();
}
}
private:
ITfThreadMgr* m_ptim;
bool m_bComInit;
};
static CDisableCicero g_disableCicero;
#define _IsLeadByte(x) ( LeadByteTable[(BYTE)( x )] )
static void _PumpMessage();
static BYTE LeadByteTable[256];
#define _ImmGetContext ImmGetContext
#define _ImmReleaseContext ImmReleaseContext
#define _ImmAssociateContext ImmAssociateContext
static LONG (WINAPI* _ImmGetCompositionString)( HIMC himc, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen );
#define _ImmGetOpenStatus ImmGetOpenStatus
#define _ImmSetOpenStatus ImmSetOpenStatus
#define _ImmGetConversionStatus ImmGetConversionStatus
static DWORD (WINAPI* _ImmGetCandidateList)( HIMC himc, DWORD deIndex, LPCANDIDATELIST lpCandList, DWORD dwBufLen );
static LPINPUTCONTEXT2 (WINAPI* _ImmLockIMC)(HIMC hIMC);
static BOOL (WINAPI* _ImmUnlockIMC)(HIMC hIMC);
static LPVOID (WINAPI* _ImmLockIMCC)(HIMCC hIMCC);
static BOOL (WINAPI* _ImmUnlockIMCC)(HIMCC hIMCC);
#define _ImmGetDefaultIMEWnd ImmGetDefaultIMEWnd
#define _ImmGetIMEFileNameA ImmGetIMEFileNameA
#define _ImmGetVirtualKey ImmGetVirtualKey
#define _ImmNotifyIME ImmNotifyIME
#define _ImmSetConversionStatus ImmSetConversionStatus
#define _ImmSimulateHotKey ImmSimulateHotKey
#define _ImmIsIME ImmIsIME
// private API provided by CHT IME. Available on version 6.0 or later.
UINT (WINAPI*_GetReadingString)(HIMC himc, UINT uReadingBufLen, LPWSTR lpwReadingBuf, PINT pnErrorIndex, BOOL* pfIsVertical, PUINT puMaxReadingLen);
BOOL (WINAPI*_ShowReadingWindow)(HIMC himc, BOOL bShow);
// Callbacks
void (CALLBACK *ImeUiCallback_DrawRect)( int x1, int y1, int x2, int y2, DWORD color );
void (CALLBACK *ImeUiCallback_DrawFans)(const IMEUI_VERTEX* paVertex, UINT uNum);
void* (__cdecl *ImeUiCallback_Malloc)( size_t bytes );
void (__cdecl *ImeUiCallback_Free)( void* ptr );
void (CALLBACK *ImeUiCallback_OnChar)( WCHAR wc );
static void (*_SendCompString)();
static LRESULT (WINAPI* _SendMessage)(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) = SendMessageA;
static DWORD (* _GetCandidateList)(HIMC himc, DWORD dwIndex, LPCANDIDATELIST* ppCandList);
static HWND g_hwndMain;
static HWND g_hwndCurr;
static HIMC g_himcOrg;
static bool g_bImeEnabled = false;
static TCHAR g_szCompositionString[256];
static BYTE g_szCompAttrString[256];
static DWORD g_IMECursorBytes = 0;
static DWORD g_IMECursorChars = 0;
static TCHAR g_szCandidate[MAX_CANDLIST][MAX_CANDIDATE_LENGTH];
static DWORD g_dwSelection, g_dwCount;
static UINT g_uCandPageSize;
static DWORD g_bDisableImeCompletely = false;
static DWORD g_dwIMELevel;
static DWORD g_dwIMELevelSaved;
static TCHAR g_szMultiLineCompString[ 256 * ( 3 - sizeof(TCHAR) ) ];
static bool g_bReadingWindow = false;
static bool g_bHorizontalReading = false;
static bool g_bVerticalCand = true;
static UINT g_uCaretBlinkTime = 0;
static UINT g_uCaretBlinkLast = 0;
static bool g_bCaretDraw = false;
static bool g_bChineseIME;
static bool g_bInsertMode = true;
static TCHAR g_szReadingString[32]; // Used only in case of horizontal reading window
static int g_iReadingError; // Used only in case of horizontal reading window
static UINT g_screenWidth, g_screenHeight;
static DWORD g_dwPrevFloat;
static bool bIsSendingKeyMessage = false;
static OSVERSIONINFOA g_osi;
static bool g_bInitialized = false;
static bool g_bCandList = false;
static DWORD g_dwCandX, g_dwCandY;
static DWORD g_dwCaretX, g_dwCaretY;
static DWORD g_hCompChar;
static int g_iCandListIndexBase;
static DWORD g_dwImeUiFlags = IMEUI_FLAG_SUPPORT_CARET;
static bool g_bUILessMode = false;
static HMODULE g_hImmDll = NULL;
#define IsNT() (g_osi.dwPlatformId == VER_PLATFORM_WIN32_NT)
struct CompStringAttribute
{
UINT caretX;
UINT caretY;
CImeUiFont_Base* pFont;
DWORD colorComp;
DWORD colorCand;
RECT margins;
};
static CompStringAttribute g_CaretInfo;
static DWORD g_dwState = IMEUI_STATE_OFF;
static DWORD swirl = 0;
static double lastSwirl;
#define INDICATOR_NON_IME 0
#define INDICATOR_CHS 1
#define INDICATOR_CHT 2
#define INDICATOR_KOREAN 3
#define INDICATOR_JAPANESE 4
#define GETLANG() LOWORD(g_hklCurrent)
#define GETPRIMLANG() ((WORD)PRIMARYLANGID(GETLANG()))
#define GETSUBLANG() SUBLANGID(GETLANG())
#define LANG_CHS MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED)
#define LANG_CHT MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_TRADITIONAL)
static HKL g_hklCurrent = 0;
static UINT g_uCodePage = 0;
static LPTSTR g_aszIndicator[] =
{
TEXT("A"),
#ifdef UNICODE
L"\x7B80",
L"\x7E41",
L"\xac00",
L"\x3042",
#else
"\xd6\xd0",
"\xa4\xa4",
"\xb0\xa1",
"\x82\xa0",
#endif
};
static LPTSTR g_pszIndicatior = g_aszIndicator[0];
static void GetReadingString(HWND hWnd);
static DWORD GetImeId( UINT uIndex = 0 );
static void CheckToggleState();
static void DrawImeIndicator();
static void DrawCandidateList();
static void DrawCompositionString( bool bDrawCompAttr );
static void GetReadingWindowOrientation( DWORD dwId );
static void OnInputLangChangeWorker();
static void OnInputLangChange();
static void SetImeApi();
static void CheckInputLocale();
static void SetSupportLevel( DWORD dwImeLevel );
void ImeUi_SetSupportLevel(DWORD dwImeLevel);
//
// local helper functions
//
inline LRESULT SendKeyMsg(HWND hwnd, UINT msg, WPARAM wp)
{
bIsSendingKeyMessage = true;
LRESULT lRc = _SendMessage(hwnd, msg, wp, 1);
bIsSendingKeyMessage = false;
return lRc;
}
#define SendKeyMsg_DOWN(hwnd,vk) SendKeyMsg(hwnd, WM_KEYDOWN, vk)
#define SendKeyMsg_UP(hwnd,vk) SendKeyMsg(hwnd, WM_KEYUP, vk)
///////////////////////////////////////////////////////////////////////////////
//
// CTsfUiLessMode
// Handles IME events using Text Service Framework (TSF). Before Vista,
// IMM (Input Method Manager) API has been used to handle IME events and
// inqueries. Some IMM functions lose backward compatibility due to design
// of TSF, so we have to use new TSF interfaces.
//
///////////////////////////////////////////////////////////////////////////////
class CTsfUiLessMode
{
protected:
// Sink receives event notifications
class CUIElementSink : public ITfUIElementSink, public ITfInputProcessorProfileActivationSink, public ITfCompartmentEventSink
{
public:
CUIElementSink();
~CUIElementSink();
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
// ITfUIElementSink
// Notifications for Reading Window events. We could process candidate as well, but we'll use IMM for simplicity sake.
STDMETHODIMP BeginUIElement(DWORD dwUIElementId, BOOL *pbShow);
STDMETHODIMP UpdateUIElement(DWORD dwUIElementId);
STDMETHODIMP EndUIElement(DWORD dwUIElementId);
// ITfInputProcessorProfileActivationSink
// Notification for keyboard input locale change
STDMETHODIMP OnActivated(DWORD dwProfileType, LANGID langid, REFCLSID clsid, REFGUID catid,
REFGUID guidProfile, HKL hkl, DWORD dwFlags);
// ITfCompartmentEventSink
// Notification for open mode (toggle state) change
STDMETHODIMP OnChange(REFGUID rguid);
private:
LONG _cRef;
};
static void MakeReadingInformationString(ITfReadingInformationUIElement* preading);
static void MakeCandidateStrings(ITfCandidateListUIElement* pcandidate);
static ITfUIElement* GetUIElement(DWORD dwUIElementId);
static BOOL GetCompartments( ITfCompartmentMgr** ppcm, ITfCompartment** ppTfOpenMode, ITfCompartment** ppTfConvMode );
static BOOL SetupCompartmentSinks( BOOL bResetOnly = FALSE, ITfCompartment* pTfOpenMode = NULL, ITfCompartment* ppTfConvMode = NULL );
static ITfThreadMgrEx* m_tm;
static DWORD m_dwUIElementSinkCookie;
static DWORD m_dwAlpnSinkCookie;
static DWORD m_dwOpenModeSinkCookie;
static DWORD m_dwConvModeSinkCookie;
static CUIElementSink *m_TsfSink;
static int m_nCandidateRefCount; // Some IME shows multiple candidate lists but the Library doesn't support multiple candidate list.
// So track open / close events to make sure the candidate list opened last is shown.
CTsfUiLessMode() {} // this class can't be instanciated
public:
static BOOL SetupSinks();
static void ReleaseSinks();
static BOOL CurrentInputLocaleIsIme();
static void UpdateImeState(BOOL bResetCompartmentEventSink = FALSE);
static void EnableUiUpdates(bool bEnable);
};
ITfThreadMgrEx* CTsfUiLessMode::m_tm;
DWORD CTsfUiLessMode::m_dwUIElementSinkCookie = TF_INVALID_COOKIE;
DWORD CTsfUiLessMode::m_dwAlpnSinkCookie = TF_INVALID_COOKIE;
DWORD CTsfUiLessMode::m_dwOpenModeSinkCookie = TF_INVALID_COOKIE;
DWORD CTsfUiLessMode::m_dwConvModeSinkCookie = TF_INVALID_COOKIE;
CTsfUiLessMode::CUIElementSink* CTsfUiLessMode::m_TsfSink = NULL;
int CTsfUiLessMode::m_nCandidateRefCount = NULL;
static unsigned long _strtoul( LPCSTR psz, LPTSTR*, int )
{
if ( !psz )
return 0;
ULONG ulRet = 0;
if ( psz[0] == '0' && ( psz[1] == 'x' || psz[1] == 'X' ) )
{
psz += 2;
ULONG ul = 0;
while ( *psz )
{
if ( '0' <= *psz && *psz <= '9' )
ul = *psz - '0';
else if ( 'A' <= *psz && *psz <= 'F' )
ul = *psz - 'A' + 10;
else if ( 'a' <= *psz && *psz <= 'f' )
ul = *psz - 'a' + 10;
else
break;
ulRet = ulRet * 16 + ul;
psz++;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -