Fix the USER32 DLL initialization and cleanup routines to prevent memory/resource...
[reactos.git] / reactos / lib / user32 / windows / accel.c
1 /*
2 * ReactOS kernel
3 * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19 /* $Id$
20 *
21 * PROJECT: ReactOS user32.dll
22 * FILE: lib/user32/windows/input.c
23 * PURPOSE: Accelerator tables
24 * PROGRAMMER: KJK::Hyperion <noog@libero.it>
25 * UPDATE HISTORY:
26 * 09/05/2001 CSH Created
27 * 08/07/2003 KJK Fully implemented
28 */
29
30 /* INCLUDES ******************************************************************/
31
32 #include <user32.h>
33
34 /* this is the 8 byte accel struct used in Win32 resources (internal only) */
35 typedef struct
36 {
37 BYTE fVirt;
38 BYTE pad0;
39 WORD key;
40 WORD cmd;
41 WORD pad1;
42 } PE_ACCEL, *LPPE_ACCEL;
43
44 /* FUNCTIONS *****************************************************************/
45
46 /* Lock guarding the cache */
47 CRITICAL_SECTION U32AccelCacheLock;
48
49 /* Cache */
50 U32_ACCEL_CACHE_ENTRY * U32AccelCache = NULL;
51
52 /* Look up a handle or resource address in the cache */
53 U32_ACCEL_CACHE_ENTRY ** WINAPI U32AccelCacheFind(HANDLE Object, HGLOBAL Data)
54 {
55 /*
56 to avoid using a double-link list and still allow elements to be removed,
57 return a pointer to the list link that points to the desired entry
58 */
59 U32_ACCEL_CACHE_ENTRY ** ppEntry = &U32AccelCache;
60
61 for(; *ppEntry; ppEntry = &((*ppEntry)->Next))
62 if((*ppEntry)->Object == Object || (*ppEntry)->Data == Data) break;
63
64 return ppEntry;
65 }
66
67 /* Allocate an entry and insert it into the cache */
68 void WINAPI U32AccelCacheAdd(HACCEL Object, HGLOBAL Data)
69 {
70 U32_ACCEL_CACHE_ENTRY * pEntry =
71 LocalAlloc(LMEM_FIXED, sizeof(U32_ACCEL_CACHE_ENTRY));
72
73 /* failed to allocate an entry - not critical */
74 if(pEntry == NULL) return;
75
76 /* initialize the entry */
77 pEntry->Usage = 1;
78 pEntry->Object = Object;
79 pEntry->Data = Data;
80
81 /* insert the entry into the cache */
82 pEntry->Next = U32AccelCache;
83 U32AccelCache = pEntry;
84 }
85
86 /* Create an accelerator table from a loaded resource */
87 HACCEL WINAPI U32LoadAccelerators(HINSTANCE hInstance, HRSRC hTableRes)
88 {
89 HGLOBAL hAccTableData;
90 HACCEL hAccTable = NULL;
91 U32_ACCEL_CACHE_ENTRY * pEntry;
92 PE_ACCEL * pAccTableResData;
93 SIZE_T i = 0;
94 SIZE_T j = 0;
95 ACCEL * pAccTableData;
96
97 /* load the accelerator table */
98 hAccTableData = LoadResource(hInstance, hTableRes);
99
100 /* failure */
101 if(hAccTableData == NULL) return NULL;
102
103 EnterCriticalSection(&U32AccelCacheLock);
104
105 /* see if this accelerator table has already been loaded */
106 pEntry = *U32AccelCacheFind(NULL, hAccTableData);
107
108 /* accelerator table already loaded */
109 if(pEntry)
110 {
111 /* increment the reference count */
112 ++ pEntry->Usage;
113
114 /* return the existing object */
115 hAccTable = pEntry->Object;
116
117 /* success */
118 goto l_Leave;
119 }
120
121 /* determine the number of entries in the table */
122 i = SizeofResource(hInstance, hTableRes) / sizeof(PE_ACCEL);
123
124 /* allocate the buffer for the table to be passed to Win32K */
125 pAccTableData = LocalAlloc(LMEM_FIXED, i * sizeof(ACCEL));
126
127 /* failure */
128 if(pAccTableData == NULL) goto l_Leave;
129
130 pAccTableResData = (PE_ACCEL *)hAccTableData;
131
132 /* copy the table */
133 for(j = 0; j < i; ++ j)
134 {
135 pAccTableData[j].fVirt = pAccTableResData[j].fVirt;
136 pAccTableData[j].key = pAccTableResData[j].key;
137 pAccTableData[j].cmd = pAccTableResData[j].cmd;
138 }
139 pAccTableData[i - 1].fVirt |= 0x80;
140
141 /* create a new accelerator table object */
142 hAccTable = NtUserCreateAcceleratorTable(pAccTableData, i);
143
144 /* free the buffer */
145 LocalFree(pAccTableData);
146
147 /* failure */
148 if(hAccTable == NULL) goto l_Leave;
149
150 /* success - cache the object */
151 U32AccelCacheAdd(hAccTable, pAccTableResData);
152
153 l_Leave:
154 LeaveCriticalSection(&U32AccelCacheLock);
155 return hAccTable;
156 }
157
158 /* Checks if a message can be translated through an accelerator table */
159 BOOL WINAPI U32IsValidAccelMessage(UINT uMsg)
160 {
161 switch(uMsg)
162 {
163 case WM_KEYDOWN:
164 case WM_KEYUP:
165 case WM_CHAR:
166 case WM_SYSKEYDOWN:
167 case WM_SYSKEYUP:
168 return TRUE;
169
170 default:
171 return FALSE;
172 }
173 }
174
175 /* WIN32 FUNCTIONS ***********************************************************/
176
177 /*
178 * Dereference the specified accelerator table, removing it from the cache and
179 * deleting the associated NtUser object as appropriate
180 *
181 * @implemented
182 */
183 BOOL WINAPI DestroyAcceleratorTable(HACCEL hAccel)
184 {
185 U32_ACCEL_CACHE_ENTRY ** ppEntry;
186 ULONG_PTR nUsage = 0;
187
188 EnterCriticalSection(&U32AccelCacheLock);
189
190 /* see if this accelerator table has been cached */
191 ppEntry = U32AccelCacheFind(hAccel, NULL);
192
193 /* accelerator table cached */
194 if(*ppEntry)
195 {
196 U32_ACCEL_CACHE_ENTRY * pEntry = *ppEntry;
197
198 /* decrement the reference count */
199 nUsage = pEntry->Usage = pEntry->Usage - 1;
200
201 /* reference count now zero: destroy the cache entry */
202 if(nUsage == 0)
203 {
204 /* unlink the cache entry */
205 *ppEntry = pEntry->Next;
206
207 /* free the cache entry */
208 LocalFree(pEntry);
209 }
210 }
211
212 LeaveCriticalSection(&U32AccelCacheLock);
213
214 if(nUsage > 0) return FALSE;
215
216 /* destroy the object */
217 return NtUserDestroyAcceleratorTable(hAccel);
218 }
219
220
221 /*
222 * Create an accelerator table from a named resource
223 *
224 * @implemented
225 */
226 HACCEL WINAPI LoadAcceleratorsW(HINSTANCE hInstance, LPCWSTR lpTableName)
227 {
228 return U32LoadAccelerators
229 (
230 hInstance,
231 FindResourceExW(hInstance, MAKEINTRESOURCEW(RT_ACCELERATOR), lpTableName, 0)
232 );
233 }
234
235
236 /*
237 * @implemented
238 */
239 HACCEL WINAPI LoadAcceleratorsA(HINSTANCE hInstance, LPCSTR lpTableName)
240 {
241 HRSRC Accel;
242
243 Accel = FindResourceExA(hInstance, MAKEINTRESOURCEA(RT_ACCELERATOR), lpTableName, 0);
244 if (NULL == Accel)
245 {
246 return NULL;
247 }
248
249 return U32LoadAccelerators(hInstance, Accel);
250 }
251
252 /*
253 * Translate a key press into a WM_COMMAND message
254 *
255 * @implemented
256 */
257 int WINAPI TranslateAcceleratorW(HWND hWnd, HACCEL hAccTable, LPMSG lpMsg)
258 {
259 if(!U32IsValidAccelMessage(lpMsg->message)) return 0;
260
261 return NtUserTranslateAccelerator(hWnd, hAccTable, lpMsg);
262 }
263
264 /*
265 * @implemented
266 */
267 int WINAPI CopyAcceleratorTableW
268 (
269 HACCEL hAccelSrc,
270 LPACCEL lpAccelDst,
271 int cAccelEntries
272 )
273 {
274 return NtUserCopyAcceleratorTable(hAccelSrc, lpAccelDst, cAccelEntries);
275 }
276
277 /*
278 * @implemented
279 */
280 HACCEL WINAPI CreateAcceleratorTableW(LPACCEL lpaccl, int cEntries)
281 {
282 return NtUserCreateAcceleratorTable(lpaccl, cEntries);
283 }
284
285
286 /*
287 * @implemented
288 */
289 int WINAPI CopyAcceleratorTableA
290 (
291 HACCEL hAccelSrc,
292 LPACCEL lpAccelDst, /* can be NULL */
293 int cAccelEntries
294 )
295 {
296 int i;
297
298 cAccelEntries = CopyAcceleratorTableW(hAccelSrc, lpAccelDst, cAccelEntries);
299
300 if (lpAccelDst == NULL) return cAccelEntries;
301
302 for(i = 0; i < cAccelEntries; ++ i)
303 if(!(lpAccelDst[i].fVirt & FVIRTKEY))
304 {
305 NTSTATUS nErrCode = RtlUnicodeToMultiByteN(
306 (PCHAR)&lpAccelDst[i].key,
307 sizeof(lpAccelDst[i].key),
308 NULL,
309 (PWCHAR)&lpAccelDst[i].key,
310 sizeof(lpAccelDst[i].key)
311 );
312
313 if(!NT_SUCCESS(nErrCode)) lpAccelDst[i].key = 0;
314 }
315
316 return cAccelEntries;
317 }
318
319
320 /*
321 * @implemented
322 */
323 HACCEL WINAPI CreateAcceleratorTableA(LPACCEL lpaccl, int cEntries)
324 {
325 int i;
326
327 for(i = 0; i < cEntries; ++ i)
328 if(!lpaccl[i].fVirt)
329 {
330 NTSTATUS nErrCode = RtlMultiByteToUnicodeN
331 (
332 (PWCHAR)&lpaccl[i].key,
333 sizeof(lpaccl[i].key),
334 NULL,
335 (PCHAR)&lpaccl[i].key,
336 sizeof(lpaccl[i].key)
337 );
338
339 if(!NT_SUCCESS(nErrCode)) lpaccl[i].key = -1;
340 }
341
342 return CreateAcceleratorTableW(lpaccl, cEntries);
343 }
344
345
346 /*
347 * @implemented
348 */
349 int WINAPI TranslateAcceleratorA(HWND hWnd, HACCEL hAccTable, LPMSG lpMsg)
350 {
351 MSG mCopy = *lpMsg;
352 CHAR cChar;
353 WCHAR wChar;
354 NTSTATUS Status;
355
356 if(!U32IsValidAccelMessage(lpMsg->message)) return 0;
357
358 Status = RtlMultiByteToUnicodeN(&wChar, sizeof(wChar), NULL, &cChar, sizeof(cChar));
359 if(!NT_SUCCESS(Status))
360 {
361 SetLastError(RtlNtStatusToDosError(Status));
362 return 0;
363 }
364
365 return TranslateAcceleratorW(hWnd, hAccTable, &mCopy);
366 }
367
368 /* EOF */