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