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