Revisión | c1bfc099f4dcea4eed11297e3aaaa911bdca8f11 (tree) |
---|---|
Tiempo | 2014-09-13 16:44:07 |
Autor | Kazuhiro Fujieda <fujieda@user...> |
Commiter | Kazuhiro Fujieda |
Support TSF-based IMEs on TSF-aware applications
XKeymacs can't recognize the start and end of a composition by a
TSF-based IME on a TSF-aware application. It therefore doesn't switch
the current configuration from the application to the IME on the start
of a composition. One of such cases is Microsoft IME and Internet
Explorer 11 on Windows 7. This patch lets XKeymacs recognize the start
and end of a composition in such cases.
@@ -36,6 +36,9 @@ void TLS::FreeLocal() | ||
36 | 36 | LPVOID data = TlsGetValue(m_TlsIndex); |
37 | 37 | if (!data) |
38 | 38 | return; |
39 | + TSFHandler *tmp = reinterpret_cast<TLS *>(data)->m_TSFHandler; | |
40 | + if (tmp) | |
41 | + tmp->Release(); | |
39 | 42 | LocalFree(data); |
40 | 43 | TlsSetValue(m_TlsIndex, nullptr); |
41 | 44 | } |
@@ -51,4 +54,17 @@ void TLS::PutKeyboardHook(HHOOK hook) | ||
51 | 54 | TLS *tls = AllocLocal(); |
52 | 55 | if (tls) |
53 | 56 | tls->m_KeyboardHook = hook; |
57 | +} | |
58 | + | |
59 | +TSFHandler *TLS::GetTSFHandler() | |
60 | +{ | |
61 | + TLS *tls = AllocLocal(); | |
62 | + return tls ? tls->m_TSFHandler : nullptr; | |
63 | +} | |
64 | + | |
65 | +void TLS::PutTSFHandler(TSFHandler *tsf) | |
66 | +{ | |
67 | + TLS *tls = AllocLocal(); | |
68 | + if (tls) | |
69 | + tls->m_TSFHandler = tsf; | |
54 | 70 | } |
\ No newline at end of file |
@@ -1,4 +1,6 @@ | ||
1 | 1 | #pragma once |
2 | +#include "TSFHandler.h" | |
3 | + | |
2 | 4 | class TLS |
3 | 5 | { |
4 | 6 | public: |
@@ -7,11 +9,13 @@ public: | ||
7 | 9 | static void FreeLocal(); |
8 | 10 | static HHOOK GetKeyboardHook(); |
9 | 11 | static void PutKeyboardHook(HHOOK hook); |
12 | + static TSFHandler *GetTSFHandler(); | |
13 | + static void PutTSFHandler(TSFHandler *tsf); | |
10 | 14 | |
11 | 15 | private: |
12 | 16 | static DWORD m_TlsIndex; |
13 | - static TLS *m_LocalData; | |
14 | - HHOOK m_KeyboardHook; | |
15 | 17 | static TLS *AllocLocal(); |
18 | + HHOOK m_KeyboardHook; | |
19 | + TSFHandler *m_TSFHandler; | |
16 | 20 | }; |
17 | 21 |
@@ -0,0 +1,194 @@ | ||
1 | +#include "StdAfx.h" | |
2 | +#include "TSFHandler.h" | |
3 | +#include "Utils.h" | |
4 | +#include "TLS.h" | |
5 | +#include "xkeymacsdll.h" | |
6 | +#include <ObjBase.h> | |
7 | + | |
8 | +#ifdef DEBUG_IME | |
9 | +#define DebugLog(fmt, ...) CUtils::Log(fmt, __VA_ARGS__) | |
10 | +#else | |
11 | +#define DebugLog(fmt, ...) | |
12 | +#endif | |
13 | + | |
14 | +TSFHandler::TSFHandler() | |
15 | +{ | |
16 | + m_RefCount = 1; | |
17 | + m_ThreadMgr = nullptr; | |
18 | + m_ClientId = 0; | |
19 | + m_Context = nullptr; | |
20 | + m_CompositionState = false; | |
21 | +} | |
22 | + | |
23 | +TSFHandler::~TSFHandler() | |
24 | +{ | |
25 | + if (m_ThreadMgr) { | |
26 | + m_ThreadMgr->Deactivate(); | |
27 | + m_ThreadMgr->Release(); | |
28 | + } | |
29 | + if (m_Context) | |
30 | + m_Context->Release(); | |
31 | +} | |
32 | + | |
33 | +void TSFHandler::InitSink() | |
34 | +{ | |
35 | + if (TLS::GetTSFHandler()) | |
36 | + return; | |
37 | + TSFHandler *tsfh = new TSFHandler(); | |
38 | + TLS::PutTSFHandler(tsfh); | |
39 | + | |
40 | + HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); | |
41 | + if (FAILED(hr)) { | |
42 | + DebugLog(_T("CoInitializeEx failed.")); | |
43 | + return; | |
44 | + } | |
45 | + if (hr == S_FALSE) | |
46 | + CoUninitialize(); | |
47 | + ITfThreadMgr *thread; | |
48 | + if (FAILED(CoCreateInstance(CLSID_TF_ThreadMgr, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&thread)))) { | |
49 | + DebugLog(_T("CoCreateInstance for ThreadMgr failed.")); | |
50 | + return; | |
51 | + } | |
52 | + tsfh->m_ThreadMgr = thread; | |
53 | + if (FAILED(thread->Activate(&tsfh->m_ClientId))) { | |
54 | + DebugLog(_T("ThreadMgr->Activate failed.")); | |
55 | + goto fail; | |
56 | + } | |
57 | + ITfSource *src; | |
58 | + if (FAILED(thread->QueryInterface(&src))) { | |
59 | + DebugLog(_T("ThreadMgr->QueryInterface failed.")); | |
60 | + goto fail; | |
61 | + } | |
62 | + if (FAILED(src->AdviseSink(IID_ITfThreadMgrEventSink, static_cast<ITfThreadMgrEventSink *>(tsfh), &tsfh->m_ClientId))) { | |
63 | + DebugLog(_T("Souece->AdviseSink failed.")); | |
64 | + src->Release(); | |
65 | + goto fail; | |
66 | + } | |
67 | + src->Release(); | |
68 | + return; | |
69 | +fail: | |
70 | + delete tsfh; | |
71 | + return; | |
72 | +} | |
73 | + | |
74 | +STDMETHODIMP TSFHandler::QueryInterface(REFIID iid, void **obj) | |
75 | +{ | |
76 | + if (obj == nullptr) | |
77 | + return E_INVALIDARG; | |
78 | + *obj = nullptr; | |
79 | + if (IsEqualIID(iid, IID_IUnknown) || iid == IID_ITfThreadMgrEventSink) | |
80 | + *obj = static_cast<ITfThreadMgrEventSink *>(this); | |
81 | + else if (iid == IID_ITfTextEditSink) | |
82 | + *obj = static_cast<ITfTextEditSink *>(this); | |
83 | + else | |
84 | + return E_NOINTERFACE; | |
85 | + AddRef(); | |
86 | + return S_OK; | |
87 | +} | |
88 | + | |
89 | +STDMETHODIMP_(ULONG) TSFHandler::AddRef() | |
90 | +{ | |
91 | + return ++m_RefCount; | |
92 | +} | |
93 | + | |
94 | +STDMETHODIMP_(ULONG) TSFHandler::Release() | |
95 | +{ | |
96 | + if (--m_RefCount == 0) | |
97 | + delete this; | |
98 | + return m_RefCount; | |
99 | +} | |
100 | + | |
101 | +// ITfThreadMgrEventSink | |
102 | +STDMETHODIMP TSFHandler::OnInitDocumentMgr(ITfDocumentMgr *) | |
103 | +{ | |
104 | + DebugLog(_T("OnInitDocumentMgr")); | |
105 | + return S_OK; | |
106 | +} | |
107 | + | |
108 | +STDMETHODIMP TSFHandler::OnPopContext(ITfContext *) | |
109 | +{ | |
110 | + DebugLog(_T("OnPopContext")); | |
111 | + return S_OK; | |
112 | +} | |
113 | + | |
114 | +STDMETHODIMP TSFHandler::OnPushContext(ITfContext *) | |
115 | +{ | |
116 | + DebugLog(_T("OnPushContext")); | |
117 | + return S_OK; | |
118 | +} | |
119 | + | |
120 | +STDMETHODIMP TSFHandler::OnSetFocus(ITfDocumentMgr *docMgr, ITfDocumentMgr *) | |
121 | +{ | |
122 | + DebugLog(_T("OnSetFocus")); | |
123 | + if (docMgr == nullptr) | |
124 | + return S_OK; | |
125 | + ITfContext *cxt; | |
126 | + if (FAILED(docMgr->GetTop(&cxt))) { | |
127 | + DebugLog(_T("DocumentMgr->GetTop failed.")); | |
128 | + return S_OK; | |
129 | + } | |
130 | + if (m_Context == cxt) | |
131 | + goto fail; | |
132 | + ITfSource *src = nullptr; | |
133 | + if (FAILED(cxt->QueryInterface(&src))) { | |
134 | + DebugLog(_T("Context->QueryInterface(ITfSource) failed.")); | |
135 | + goto fail; | |
136 | + } | |
137 | + DWORD cookie; | |
138 | + if (FAILED(src->AdviseSink(IID_ITfTextEditSink, static_cast<ITfTextEditSink *>(this), &cookie))) { | |
139 | + DebugLog(_T("Source->AdviseSink(ITfTextEditSink) failed.")); | |
140 | + src->Release(); | |
141 | + goto fail; | |
142 | + } | |
143 | + src->Release(); | |
144 | + m_Context = cxt; | |
145 | + return S_OK; | |
146 | +fail: | |
147 | + cxt->Release(); | |
148 | + return S_OK; | |
149 | +} | |
150 | + | |
151 | +STDMETHODIMP TSFHandler::OnUninitDocumentMgr(ITfDocumentMgr *) | |
152 | +{ | |
153 | + DebugLog(_T("OnUninitDocumentMgr")); | |
154 | + return S_OK; | |
155 | +} | |
156 | + | |
157 | +STDMETHODIMP TSFHandler::OnEndEdit(ITfContext *cxt, TfEditCookie, ITfEditRecord *) | |
158 | +{ | |
159 | + DebugLog(_T("OnEndEdit")); | |
160 | + ITfContextComposition *comp; | |
161 | + if (FAILED(cxt->QueryInterface(&comp))) { | |
162 | + DebugLog(_T("Context->QueryInterface(ITfContextComposition) failed.")); | |
163 | + return S_OK; | |
164 | + } | |
165 | + IEnumITfCompositionView *enumCompView; | |
166 | + if (FAILED(comp->EnumCompositions(&enumCompView))) { | |
167 | + DebugLog(_T("ContextComposition->EnumCompositions failed.")); | |
168 | + goto fail; | |
169 | + } | |
170 | + ITfCompositionView *view; | |
171 | + ULONG fetched; | |
172 | + HRESULT hr = enumCompView->Next(1, &view, &fetched); | |
173 | + if (FAILED(hr)) { | |
174 | + DebugLog(_T("EnumCompositions->Next failed.")); | |
175 | + enumCompView->Release(); | |
176 | + goto fail; | |
177 | + } | |
178 | + DebugLog(_T("EnumComposition->Next succeeded. fetched=%d"), fetched); | |
179 | + if (fetched) { | |
180 | + if (!m_CompositionState) | |
181 | + CXkeymacsDll::SetIMEState(true); | |
182 | + m_CompositionState = true; | |
183 | + view->Release(); | |
184 | + } | |
185 | + else { | |
186 | + if (m_CompositionState) | |
187 | + CXkeymacsDll::SetIMEState(false); | |
188 | + m_CompositionState = false; | |
189 | + } | |
190 | + enumCompView->Release(); | |
191 | +fail: | |
192 | + comp->Release(); | |
193 | + return S_OK; | |
194 | +} | |
\ No newline at end of file |
@@ -0,0 +1,35 @@ | ||
1 | +#pragma once | |
2 | + | |
3 | +#include <msctf.h> | |
4 | + | |
5 | +class TSFHandler: public ITfThreadMgrEventSink, public ITfTextEditSink | |
6 | +{ | |
7 | +public: | |
8 | + TSFHandler(); | |
9 | + ~TSFHandler(); | |
10 | + static void InitSink(); | |
11 | + static void SetFocus(); | |
12 | + | |
13 | + // IUnknown | |
14 | + STDMETHODIMP QueryInterface(REFIID riid, void **obj); | |
15 | + STDMETHODIMP_(ULONG) AddRef(); | |
16 | + STDMETHODIMP_(ULONG) Release(); | |
17 | + | |
18 | + // ITfThreadMgrEventSink | |
19 | + STDMETHODIMP OnInitDocumentMgr(ITfDocumentMgr *); | |
20 | + STDMETHODIMP OnPopContext(ITfContext *); | |
21 | + STDMETHODIMP OnPushContext(ITfContext *); | |
22 | + STDMETHODIMP OnSetFocus(ITfDocumentMgr *, ITfDocumentMgr *); | |
23 | + STDMETHODIMP OnUninitDocumentMgr(ITfDocumentMgr *); | |
24 | + | |
25 | + // ITfTextEditSink | |
26 | + STDMETHODIMP OnEndEdit(ITfContext *, TfEditCookie , ITfEditRecord *); | |
27 | + | |
28 | +private: | |
29 | + ULONG m_RefCount; | |
30 | + ITfThreadMgr *m_ThreadMgr; | |
31 | + TfClientId m_ClientId; | |
32 | + ITfContext *m_Context; | |
33 | + bool m_CompositionState; | |
34 | +}; | |
35 | + |
@@ -216,6 +216,7 @@ | ||
216 | 216 | <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader> |
217 | 217 | </ClCompile> |
218 | 218 | <ClCompile Include="TLS.cpp" /> |
219 | + <ClCompile Include="TSFHandler.cpp" /> | |
219 | 220 | <ClCompile Include="Utils.cpp" /> |
220 | 221 | <ClCompile Include="xkeymacsdll.cpp" /> |
221 | 222 | </ItemGroup> |
@@ -236,6 +237,7 @@ | ||
236 | 237 | <ClInclude Include="resource.h" /> |
237 | 238 | <ClInclude Include="StdAfx.h" /> |
238 | 239 | <ClInclude Include="TLS.h" /> |
240 | + <ClInclude Include="TSFHandler.h" /> | |
239 | 241 | <ClInclude Include="tstring.h" /> |
240 | 242 | <ClInclude Include="Utils.h" /> |
241 | 243 | <ClInclude Include="xkeymacsdll.h" /> |
@@ -51,6 +51,9 @@ | ||
51 | 51 | <ClCompile Include="TLS.cpp"> |
52 | 52 | <Filter>Source Files</Filter> |
53 | 53 | </ClCompile> |
54 | + <ClCompile Include="TSFHandler.cpp"> | |
55 | + <Filter>Source Files</Filter> | |
56 | + </ClCompile> | |
54 | 57 | </ItemGroup> |
55 | 58 | <ItemGroup> |
56 | 59 | <None Include="xkeymacsdll.def"> |
@@ -106,6 +109,9 @@ | ||
106 | 109 | <ClInclude Include="TLS.h"> |
107 | 110 | <Filter>Header Files</Filter> |
108 | 111 | </ClInclude> |
112 | + <ClInclude Include="TSFHandler.h"> | |
113 | + <Filter>Header Files</Filter> | |
114 | + </ClInclude> | |
109 | 115 | </ItemGroup> |
110 | 116 | <ItemGroup> |
111 | 117 | <ResourceCompile Include="xkeymacsdll.rc"> |
@@ -6,6 +6,7 @@ | ||
6 | 6 | #include "Commands.h" |
7 | 7 | #include "CmdTable.h" |
8 | 8 | #include "TLS.h" |
9 | +#include "TSFHandler.h" | |
9 | 10 | #include "../xkeymacs/resource.h" |
10 | 11 | #include <math.h> |
11 | 12 | #include <Imm.h> |
@@ -242,26 +243,29 @@ void CXkeymacsDll::ShowHookState() | ||
242 | 243 | LRESULT CALLBACK CXkeymacsDll::CallWndProc(int nCode, WPARAM wParam, LPARAM lParam) |
243 | 244 | { |
244 | 245 | SetKeyboardHook(); |
246 | + TSFHandler::InitSink(); | |
245 | 247 | if (nCode >= 0) { |
246 | 248 | const CWPSTRUCT *cwps = reinterpret_cast<CWPSTRUCT *>(lParam); |
247 | 249 | switch (cwps->message) { |
248 | 250 | case WM_IME_STARTCOMPOSITION: |
249 | - AppName::SetIMEState(true); | |
250 | - InitKeyboardProc(); | |
251 | +#ifdef DEBUG_IME | |
252 | + CUtils::Log(_T("WM_IME_STARTCOMPOSITION")); | |
253 | +#endif | |
254 | + SetIMEState(true); | |
251 | 255 | break; |
252 | 256 | case WM_IME_ENDCOMPOSITION: |
253 | - AppName::SetIMEState(false); | |
254 | - InitKeyboardProc(); | |
257 | +#ifdef DEBUG_IME | |
258 | + CUtils::Log(_T("WM_IME_ENDCOMPOSITION")); | |
259 | +#endif | |
260 | + SetIMEState(false); | |
255 | 261 | break; |
256 | 262 | case WM_SETFOCUS: |
257 | - AppName::SetIMEState(false); | |
258 | - InitKeyboardProc(); | |
263 | + SetIMEState(false); | |
259 | 264 | ShowHookState(); |
260 | 265 | break; |
261 | 266 | case WM_NCACTIVATE: |
262 | 267 | if (cwps->wParam && cwps->hwnd == GetForegroundWindow()) { |
263 | - AppName::SetIMEState(false); | |
264 | - InitKeyboardProc(); | |
268 | + SetIMEState(false); | |
265 | 269 | ShowHookState(); |
266 | 270 | } |
267 | 271 | break; |
@@ -295,18 +299,28 @@ LRESULT CALLBACK CXkeymacsDll::GetMsgProc(int nCode, WPARAM wParam, LPARAM lPara | ||
295 | 299 | const MSG *msg = reinterpret_cast<MSG *>(lParam); |
296 | 300 | switch (msg->message) { |
297 | 301 | case WM_IME_STARTCOMPOSITION: |
298 | - AppName::SetIMEState(true); | |
299 | - InitKeyboardProc(); | |
302 | +#ifdef DEBUG_IME | |
303 | + CUtils::Log(_T("WM_IME_STARTCOMPOSITION")); | |
304 | +#endif | |
305 | + SetIMEState(true); | |
300 | 306 | break; |
301 | 307 | case WM_IME_ENDCOMPOSITION: |
302 | - AppName::SetIMEState(false); | |
303 | - InitKeyboardProc(); | |
308 | +#ifdef DEBUG_IME | |
309 | + CUtils::Log(_T("WM_IME_ENDCOMPOSITION")); | |
310 | +#endif | |
311 | + SetIMEState(false); | |
304 | 312 | break; |
305 | 313 | } |
306 | 314 | } |
307 | 315 | return CallNextHookEx(NULL, nCode, wParam, lParam); |
308 | 316 | } |
309 | 317 | |
318 | +void CXkeymacsDll::SetIMEState(bool on) | |
319 | +{ | |
320 | + AppName::SetIMEState(on); | |
321 | + InitKeyboardProc(); | |
322 | +} | |
323 | + | |
310 | 324 | LRESULT CALLBACK CXkeymacsDll::ShellProc(int nCode, WPARAM wParam, LPARAM lParam) |
311 | 325 | { |
312 | 326 | if (nCode == HSHELL_WINDOWACTIVATED) { |
@@ -24,7 +24,6 @@ public: | ||
24 | 24 | static BOOL LoadConfig(); |
25 | 25 | static void SetConfig(const Config& config); |
26 | 26 | static void SetHooks(); |
27 | - static void SetKeyboardHook(DWORD threadId = 0); | |
28 | 27 | static void ResetHooks(); |
29 | 28 | static void ReleaseHooks(); |
30 | 29 | static void ReleaseKeyboardHook(); |
@@ -32,6 +31,7 @@ public: | ||
32 | 31 | static void ToggleHookState(); |
33 | 32 | static bool GetHookState(); |
34 | 33 | static void ShowHookState(); |
34 | + static void SetIMEState(bool on); | |
35 | 35 | static void SetM_xTip(LPCTSTR szPath); |
36 | 36 | static void SendIconMessage(IconState *state, int num); |
37 | 37 | static BOOL IsDown(BYTE bVk, BOOL bPhysicalKey = TRUE); |
@@ -64,6 +64,7 @@ private: | ||
64 | 64 | static HHOOK m_hHookGetMessage; |
65 | 65 | static HHOOK m_hHookShell; |
66 | 66 | static bool m_bEnableKeyboardHook; |
67 | + static void SetKeyboardHook(DWORD threadId = 0); | |
67 | 68 | static bool m_bHook; |
68 | 69 | static void SetHookState(bool enable); |
69 | 70 | static DWORD m_nHookAltRelease; |