Create a branch for Aleksandar Andrejevic for his work on NTVDM. See http://jira...
[reactos.git] / dll / win32 / msctf / msctf.c
1 /*
2 * MSCTF Server DLL
3 *
4 * Copyright 2008 Aric Stewart, CodeWeavers
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #define WIN32_NO_STATUS
22 #define _INC_WINDOWS
23 #define COM_NO_WINDOWS_H
24
25 #include <config.h>
26
27 //#include <stdarg.h>
28 //#include <stdio.h>
29
30 #define COBJMACROS
31
32 #include <wine/debug.h>
33 #include <wine/list.h>
34 //#include "windef.h"
35 #include <winbase.h>
36 #include <winreg.h>
37 #include <shlwapi.h>
38 //#include "shlguid.h"
39 //#include "comcat.h"
40 #include <rpcproxy.h>
41 #include <msctf.h>
42
43 #include "msctf_internal.h"
44
45 WINE_DEFAULT_DEBUG_CHANNEL(msctf);
46
47 static LONG MSCTF_refCount;
48
49 static HINSTANCE MSCTF_hinstance;
50
51 typedef struct
52 {
53 DWORD id;
54 DWORD magic;
55 LPVOID data;
56 } CookieInternal;
57
58 typedef struct {
59 TF_LANGUAGEPROFILE LanguageProfile;
60 ITfTextInputProcessor *pITfTextInputProcessor;
61 ITfThreadMgr *pITfThreadMgr;
62 ITfKeyEventSink *pITfKeyEventSink;
63 TfClientId tid;
64 } ActivatedTextService;
65
66 typedef struct
67 {
68 struct list entry;
69 ActivatedTextService *ats;
70 } AtsEntry;
71
72 static CookieInternal *cookies;
73 static UINT id_last;
74 static UINT array_size;
75
76 static struct list AtsList = LIST_INIT(AtsList);
77 static UINT activated = 0;
78
79 DWORD tlsIndex = 0;
80 TfClientId processId = 0;
81 ITfCompartmentMgr *globalCompartmentMgr = NULL;
82
83 const WCHAR szwSystemTIPKey[] = {'S','O','F','T','W','A','R','E','\\','M','i','c','r','o','s','o','f','t','\\','C','T','F','\\','T','I','P',0};
84 const WCHAR szwSystemCTFKey[] = {'S','O','F','T','W','A','R','E','\\','M','i','c','r','o','s','o','f','t','\\','C','T','F',0};
85
86 typedef HRESULT (*LPFNCONSTRUCTOR)(IUnknown *pUnkOuter, IUnknown **ppvOut);
87
88 static const struct {
89 REFCLSID clsid;
90 LPFNCONSTRUCTOR ctor;
91 } ClassesTable[] = {
92 {&CLSID_TF_ThreadMgr, ThreadMgr_Constructor},
93 {&CLSID_TF_InputProcessorProfiles, InputProcessorProfiles_Constructor},
94 {&CLSID_TF_CategoryMgr, CategoryMgr_Constructor},
95 {&CLSID_TF_LangBarMgr, LangBarMgr_Constructor},
96 {&CLSID_TF_DisplayAttributeMgr, DisplayAttributeMgr_Constructor},
97 {NULL, NULL}
98 };
99
100 typedef struct tagClassFactory
101 {
102 IClassFactory IClassFactory_iface;
103 LONG ref;
104 LPFNCONSTRUCTOR ctor;
105 } ClassFactory;
106
107 static inline ClassFactory *impl_from_IClassFactory(IClassFactory *iface)
108 {
109 return CONTAINING_RECORD(iface, ClassFactory, IClassFactory_iface);
110 }
111
112 static void ClassFactory_Destructor(ClassFactory *This)
113 {
114 TRACE("Destroying class factory %p\n", This);
115 HeapFree(GetProcessHeap(),0,This);
116 MSCTF_refCount--;
117 }
118
119 static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, LPVOID *ppvOut)
120 {
121 *ppvOut = NULL;
122 if (IsEqualIID(riid, &IID_IClassFactory) || IsEqualIID(riid, &IID_IUnknown)) {
123 IClassFactory_AddRef(iface);
124 *ppvOut = iface;
125 return S_OK;
126 }
127
128 WARN("Unknown interface %s\n", debugstr_guid(riid));
129 return E_NOINTERFACE;
130 }
131
132 static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface)
133 {
134 ClassFactory *This = impl_from_IClassFactory(iface);
135 return InterlockedIncrement(&This->ref);
136 }
137
138 static ULONG WINAPI ClassFactory_Release(IClassFactory *iface)
139 {
140 ClassFactory *This = impl_from_IClassFactory(iface);
141 ULONG ret = InterlockedDecrement(&This->ref);
142
143 if (ret == 0)
144 ClassFactory_Destructor(This);
145 return ret;
146 }
147
148 static HRESULT WINAPI ClassFactory_CreateInstance(IClassFactory *iface, IUnknown *punkOuter, REFIID iid, LPVOID *ppvOut)
149 {
150 ClassFactory *This = impl_from_IClassFactory(iface);
151 HRESULT ret;
152 IUnknown *obj;
153
154 TRACE("(%p, %p, %s, %p)\n", iface, punkOuter, debugstr_guid(iid), ppvOut);
155 ret = This->ctor(punkOuter, &obj);
156 if (FAILED(ret))
157 return ret;
158 ret = IUnknown_QueryInterface(obj, iid, ppvOut);
159 IUnknown_Release(obj);
160 return ret;
161 }
162
163 static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL fLock)
164 {
165 ClassFactory *This = impl_from_IClassFactory(iface);
166
167 TRACE("(%p)->(%x)\n", This, fLock);
168
169 if(fLock)
170 InterlockedIncrement(&MSCTF_refCount);
171 else
172 InterlockedDecrement(&MSCTF_refCount);
173
174 return S_OK;
175 }
176
177 static const IClassFactoryVtbl ClassFactoryVtbl = {
178 /* IUnknown */
179 ClassFactory_QueryInterface,
180 ClassFactory_AddRef,
181 ClassFactory_Release,
182
183 /* IClassFactory*/
184 ClassFactory_CreateInstance,
185 ClassFactory_LockServer
186 };
187
188 static HRESULT ClassFactory_Constructor(LPFNCONSTRUCTOR ctor, LPVOID *ppvOut)
189 {
190 ClassFactory *This = HeapAlloc(GetProcessHeap(),0,sizeof(ClassFactory));
191 This->IClassFactory_iface.lpVtbl = &ClassFactoryVtbl;
192 This->ref = 1;
193 This->ctor = ctor;
194 *ppvOut = This;
195 TRACE("Created class factory %p\n", This);
196 MSCTF_refCount++;
197 return S_OK;
198 }
199
200 /*************************************************************************
201 * DWORD Cookie Management
202 */
203 DWORD generate_Cookie(DWORD magic, LPVOID data)
204 {
205 int i;
206
207 /* try to reuse IDs if possible */
208 for (i = 0; i < id_last; i++)
209 if (cookies[i].id == 0) break;
210
211 if (i == array_size)
212 {
213 if (!array_size)
214 {
215 cookies = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(CookieInternal) * 10);
216 if (!cookies)
217 {
218 ERR("Out of memory, Unable to alloc cookies array\n");
219 return 0;
220 }
221 array_size = 10;
222 }
223 else
224 {
225 CookieInternal *new_cookies = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cookies,
226 sizeof(CookieInternal) * (array_size * 2));
227 if (!new_cookies)
228 {
229 ERR("Out of memory, Unable to realloc cookies array\n");
230 return 0;
231 }
232 cookies = new_cookies;
233 array_size *= 2;
234 }
235 }
236
237 cookies[i].id = i + 1; /* a return of 0 is used for failure */
238 cookies[i].magic = magic;
239 cookies[i].data = data;
240
241 if (i == id_last)
242 id_last++;
243
244 return cookies[i].id;
245 }
246
247 DWORD get_Cookie_magic(DWORD id)
248 {
249 UINT index = id - 1;
250
251 if (index >= id_last)
252 return 0;
253
254 if (cookies[index].id == 0)
255 return 0;
256
257 return cookies[index].magic;
258 }
259
260 LPVOID get_Cookie_data(DWORD id)
261 {
262 UINT index = id - 1;
263
264 if (index >= id_last)
265 return NULL;
266
267 if (cookies[index].id == 0)
268 return NULL;
269
270 return cookies[index].data;
271 }
272
273 LPVOID remove_Cookie(DWORD id)
274 {
275 UINT index = id - 1;
276
277 if (index >= id_last)
278 return NULL;
279
280 if (cookies[index].id == 0)
281 return NULL;
282
283 cookies[index].id = 0;
284 return cookies[index].data;
285 }
286
287 DWORD enumerate_Cookie(DWORD magic, DWORD *index)
288 {
289 int i;
290 for (i = *index; i < id_last; i++)
291 if (cookies[i].id != 0 && cookies[i].magic == magic)
292 {
293 *index = (i+1);
294 return cookies[i].id;
295 }
296 return 0x0;
297 }
298
299 /*****************************************************************************
300 * Active Text Service Management
301 *****************************************************************************/
302 static HRESULT activate_given_ts(ActivatedTextService *actsvr, ITfThreadMgr* tm)
303 {
304 HRESULT hr;
305
306 /* Already Active? */
307 if (actsvr->pITfTextInputProcessor)
308 return S_OK;
309
310 hr = CoCreateInstance (&actsvr->LanguageProfile.clsid, NULL, CLSCTX_INPROC_SERVER,
311 &IID_ITfTextInputProcessor, (void**)&actsvr->pITfTextInputProcessor);
312 if (FAILED(hr)) return hr;
313
314 hr = ITfTextInputProcessor_Activate(actsvr->pITfTextInputProcessor, tm, actsvr->tid);
315 if (FAILED(hr))
316 {
317 ITfTextInputProcessor_Release(actsvr->pITfTextInputProcessor);
318 actsvr->pITfTextInputProcessor = NULL;
319 return hr;
320 }
321
322 actsvr->pITfThreadMgr = tm;
323 ITfThreadMgr_AddRef(tm);
324 return hr;
325 }
326
327 static HRESULT deactivate_given_ts(ActivatedTextService *actsvr)
328 {
329 HRESULT hr = S_OK;
330
331 if (actsvr->pITfTextInputProcessor)
332 {
333 hr = ITfTextInputProcessor_Deactivate(actsvr->pITfTextInputProcessor);
334 ITfTextInputProcessor_Release(actsvr->pITfTextInputProcessor);
335 ITfThreadMgr_Release(actsvr->pITfThreadMgr);
336 actsvr->pITfTextInputProcessor = NULL;
337 actsvr->pITfThreadMgr = NULL;
338 }
339
340 return hr;
341 }
342
343 static void deactivate_remove_conflicting_ts(REFCLSID catid)
344 {
345 AtsEntry *ats, *cursor2;
346
347 LIST_FOR_EACH_ENTRY_SAFE(ats, cursor2, &AtsList, AtsEntry, entry)
348 {
349 if (IsEqualCLSID(catid,&ats->ats->LanguageProfile.catid))
350 {
351 deactivate_given_ts(ats->ats);
352 list_remove(&ats->entry);
353 HeapFree(GetProcessHeap(),0,ats->ats);
354 HeapFree(GetProcessHeap(),0,ats);
355 /* we are guarenteeing there is only 1 */
356 break;
357 }
358 }
359 }
360
361 HRESULT add_active_textservice(TF_LANGUAGEPROFILE *lp)
362 {
363 ActivatedTextService *actsvr;
364 ITfCategoryMgr *catmgr;
365 AtsEntry *entry;
366 ITfThreadMgr *tm = TlsGetValue(tlsIndex);
367 ITfClientId *clientid;
368
369 if (!tm) return E_UNEXPECTED;
370
371 actsvr = HeapAlloc(GetProcessHeap(),0,sizeof(ActivatedTextService));
372 if (!actsvr) return E_OUTOFMEMORY;
373
374 ITfThreadMgr_QueryInterface(tm,&IID_ITfClientId,(LPVOID)&clientid);
375 ITfClientId_GetClientId(clientid, &lp->clsid, &actsvr->tid);
376 ITfClientId_Release(clientid);
377
378 if (!actsvr->tid)
379 {
380 HeapFree(GetProcessHeap(),0,actsvr);
381 return E_OUTOFMEMORY;
382 }
383
384 actsvr->pITfTextInputProcessor = NULL;
385 actsvr->LanguageProfile = *lp;
386 actsvr->LanguageProfile.fActive = TRUE;
387 actsvr->pITfKeyEventSink = NULL;
388
389 /* get TIP category */
390 if (SUCCEEDED(CategoryMgr_Constructor(NULL,(IUnknown**)&catmgr)))
391 {
392 static const GUID *list[3] = {&GUID_TFCAT_TIP_SPEECH, &GUID_TFCAT_TIP_KEYBOARD, &GUID_TFCAT_TIP_HANDWRITING};
393
394 ITfCategoryMgr_FindClosestCategory(catmgr,
395 &actsvr->LanguageProfile.clsid, &actsvr->LanguageProfile.catid,
396 list, 3);
397
398 ITfCategoryMgr_Release(catmgr);
399 }
400 else
401 {
402 ERR("CategoryMgr construction failed\n");
403 actsvr->LanguageProfile.catid = GUID_NULL;
404 }
405
406 if (!IsEqualGUID(&actsvr->LanguageProfile.catid,&GUID_NULL))
407 deactivate_remove_conflicting_ts(&actsvr->LanguageProfile.catid);
408
409 if (activated > 0)
410 activate_given_ts(actsvr, tm);
411
412 entry = HeapAlloc(GetProcessHeap(),0,sizeof(AtsEntry));
413
414 if (!entry)
415 {
416 HeapFree(GetProcessHeap(),0,actsvr);
417 return E_OUTOFMEMORY;
418 }
419
420 entry->ats = actsvr;
421 list_add_head(&AtsList, &entry->entry);
422
423 return S_OK;
424 }
425
426 BOOL get_active_textservice(REFCLSID rclsid, TF_LANGUAGEPROFILE *profile)
427 {
428 AtsEntry *ats;
429
430 LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry)
431 {
432 if (IsEqualCLSID(rclsid,&ats->ats->LanguageProfile.clsid))
433 {
434 if (profile)
435 *profile = ats->ats->LanguageProfile;
436 return TRUE;
437 }
438 }
439 return FALSE;
440 }
441
442 HRESULT activate_textservices(ITfThreadMgr *tm)
443 {
444 HRESULT hr = S_OK;
445 AtsEntry *ats;
446
447 activated ++;
448 if (activated > 1)
449 return S_OK;
450
451 LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry)
452 {
453 hr = activate_given_ts(ats->ats, tm);
454 if (FAILED(hr))
455 FIXME("Failed to activate text service\n");
456 }
457 return hr;
458 }
459
460 HRESULT deactivate_textservices(void)
461 {
462 AtsEntry *ats;
463
464 if (activated > 0)
465 activated --;
466
467 if (activated == 0)
468 LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry)
469 deactivate_given_ts(ats->ats);
470
471 return S_OK;
472 }
473
474 CLSID get_textservice_clsid(TfClientId tid)
475 {
476 AtsEntry *ats;
477
478 LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry)
479 if (ats->ats->tid == tid)
480 return ats->ats->LanguageProfile.clsid;
481 return GUID_NULL;
482 }
483
484 HRESULT get_textservice_sink(TfClientId tid, REFCLSID iid, IUnknown **sink)
485 {
486 AtsEntry *ats;
487
488 if (!IsEqualCLSID(iid,&IID_ITfKeyEventSink))
489 return E_NOINTERFACE;
490
491 LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry)
492 if (ats->ats->tid == tid)
493 {
494 *sink = (IUnknown*)ats->ats->pITfKeyEventSink;
495 return S_OK;
496 }
497
498 return E_FAIL;
499 }
500
501 HRESULT set_textservice_sink(TfClientId tid, REFCLSID iid, IUnknown* sink)
502 {
503 AtsEntry *ats;
504
505 if (!IsEqualCLSID(iid,&IID_ITfKeyEventSink))
506 return E_NOINTERFACE;
507
508 LIST_FOR_EACH_ENTRY(ats, &AtsList, AtsEntry, entry)
509 if (ats->ats->tid == tid)
510 {
511 ats->ats->pITfKeyEventSink = (ITfKeyEventSink*)sink;
512 return S_OK;
513 }
514
515 return E_FAIL;
516 }
517
518 /*************************************************************************
519 * MSCTF DllMain
520 */
521 BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID fImpLoad)
522 {
523 TRACE("%p 0x%x %p\n", hinst, fdwReason, fImpLoad);
524 switch (fdwReason)
525 {
526 case DLL_WINE_PREATTACH:
527 return FALSE; /* prefer native version */
528 case DLL_PROCESS_ATTACH:
529 MSCTF_hinstance = hinst;
530 tlsIndex = TlsAlloc();
531 break;
532 case DLL_PROCESS_DETACH:
533 TlsFree(tlsIndex);
534 break;
535 }
536 return TRUE;
537 }
538
539 /*************************************************************************
540 * DllCanUnloadNow (MSCTF.@)
541 */
542 HRESULT WINAPI DllCanUnloadNow(void)
543 {
544 return MSCTF_refCount ? S_FALSE : S_OK;
545 }
546
547 /***********************************************************************
548 * DllGetClassObject (MSCTF.@)
549 */
550 HRESULT WINAPI DllGetClassObject(REFCLSID clsid, REFIID iid, LPVOID *ppvOut)
551 {
552 int i;
553
554 *ppvOut = NULL;
555 if (!IsEqualIID(iid, &IID_IUnknown) && !IsEqualIID(iid, &IID_IClassFactory))
556 return E_NOINTERFACE;
557
558 for (i = 0; ClassesTable[i].clsid != NULL; i++)
559 if (IsEqualCLSID(ClassesTable[i].clsid, clsid)) {
560 return ClassFactory_Constructor(ClassesTable[i].ctor, ppvOut);
561 }
562 FIXME("CLSID %s not supported\n", debugstr_guid(clsid));
563 return CLASS_E_CLASSNOTAVAILABLE;
564 }
565
566 /***********************************************************************
567 * DllRegisterServer (MSCTF.@)
568 */
569 HRESULT WINAPI DllRegisterServer(void)
570 {
571 return __wine_register_resources( MSCTF_hinstance );
572 }
573
574 /***********************************************************************
575 * DllUnregisterServer (MSCTF.@)
576 */
577 HRESULT WINAPI DllUnregisterServer(void)
578 {
579 return __wine_unregister_resources( MSCTF_hinstance );
580 }
581
582 /***********************************************************************
583 * TF_CreateThreadMgr (MSCTF.@)
584 */
585 HRESULT WINAPI TF_CreateThreadMgr(ITfThreadMgr **pptim)
586 {
587 TRACE("\n");
588 return ThreadMgr_Constructor(NULL,(IUnknown**)pptim);
589 }
590
591 /***********************************************************************
592 * TF_GetThreadMgr (MSCTF.@)
593 */
594 HRESULT WINAPI TF_GetThreadMgr(ITfThreadMgr **pptim)
595 {
596 TRACE("\n");
597 *pptim = TlsGetValue(tlsIndex);
598
599 if (*pptim)
600 ITfThreadMgr_AddRef(*pptim);
601
602 return S_OK;
603 }
604
605 /***********************************************************************
606 * SetInputScope(MSCTF.@)
607 */
608 HRESULT WINAPI SetInputScope(HWND hwnd, INT inputscope)
609 {
610 FIXME("STUB: %p %i\n",hwnd,inputscope);
611 return S_OK;
612 }
613
614 /***********************************************************************
615 * SetInputScopes(MSCTF.@)
616 */
617 HRESULT WINAPI SetInputScopes(HWND hwnd, const INT *pInputScopes,
618 UINT cInputScopes, WCHAR **ppszPhraseList,
619 UINT cPhrases, WCHAR *pszRegExp, WCHAR *pszSRGS)
620 {
621 int i;
622 FIXME("STUB: %p ... %s %s\n",hwnd, debugstr_w(pszRegExp), debugstr_w(pszSRGS));
623 for (i = 0; i < cInputScopes; i++)
624 TRACE("\tScope[%i] = %i\n",i,pInputScopes[i]);
625 for (i = 0; i < cPhrases; i++)
626 TRACE("\tPhrase[%i] = %s\n",i,debugstr_w(ppszPhraseList[i]));
627
628 return S_OK;
629 }
630
631 /***********************************************************************
632 * TF_CreateInputProcessorProfiles(MSCTF.@)
633 */
634 HRESULT WINAPI TF_CreateInputProcessorProfiles(
635 ITfInputProcessorProfiles **ppipr)
636 {
637 return InputProcessorProfiles_Constructor(NULL,(IUnknown**)ppipr);
638 }
639
640 /***********************************************************************
641 * TF_InvalidAssemblyListCacheIfExist(MSCTF.@)
642 */
643 HRESULT WINAPI TF_InvalidAssemblyListCacheIfExist(void)
644 {
645 FIXME("Stub\n");
646 return S_OK;
647 }
648
649 /***********************************************************************
650 * TF_CreateLangBarMgr (MSCTF.@)
651 */
652 HRESULT WINAPI TF_CreateLangBarMgr(ITfLangBarMgr **pppbm)
653 {
654 TRACE("\n");
655 return LangBarMgr_Constructor(NULL,(IUnknown**)pppbm);
656 }