e7e820b8a9269174146d5bd9a59f60e994b73bdc
[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: accel.c,v 1.8 2003/07/10 21:04:31 chorns Exp $
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 #include <string.h>
32 #include <windows.h>
33 #include <user32.h>
34 #include <debug.h>
35
36 /* FUNCTIONS *****************************************************************/
37
38 /* RT_ACCELERATOR resources are arrays of RES_ACCEL structures */
39 typedef struct _RES_ACCEL
40 {
41 WORD fVirt;
42 WORD key;
43 DWORD cmd;
44 }
45 RES_ACCEL;
46
47 /* ACCELERATOR TABLES CACHE */
48 /* Cache entry */
49 typedef struct _USER_ACCEL_CACHE_ENTRY
50 {
51 struct _USER_ACCEL_CACHE_ENTRY * Next;
52 ULONG_PTR Usage; /* how many times the table has been loaded */
53 HACCEL Object; /* handle to the NtUser accelerator table object */
54 HGLOBAL Data; /* base address of the resource data */
55 }
56 U32_ACCEL_CACHE_ENTRY;
57
58 /* Lock guarding the cache */
59 CRITICAL_SECTION U32AccelCacheLock;
60
61 /* Cache */
62 U32_ACCEL_CACHE_ENTRY * U32AccelCache = NULL;
63
64 U32_ACCEL_CACHE_ENTRY ** WINAPI U32AccelCacheFind(HANDLE, HGLOBAL);
65 void WINAPI U32AccelCacheAdd(HACCEL, HGLOBAL);
66
67 /* Look up a handle or resource address in the cache */
68 U32_ACCEL_CACHE_ENTRY ** WINAPI U32AccelCacheFind(HANDLE Object, HGLOBAL Data)
69 {
70 /*
71 to avoid using a double-link list and still allow elements to be removed,
72 return a pointer to the list link that points to the desired entry
73 */
74 U32_ACCEL_CACHE_ENTRY ** ppEntry = &U32AccelCache;
75
76 for(; *ppEntry; ppEntry = &((*ppEntry)->Next))
77 if((*ppEntry)->Object == Object || (*ppEntry)->Data == Data) break;
78
79 return ppEntry;
80 }
81
82 /* Allocate an entry and insert it into the cache */
83 void WINAPI U32AccelCacheAdd(HACCEL Object, HGLOBAL Data)
84 {
85 U32_ACCEL_CACHE_ENTRY * pEntry =
86 LocalAlloc(LMEM_FIXED, sizeof(U32_ACCEL_CACHE_ENTRY));
87
88 /* failed to allocate an entry - not critical */
89 if(pEntry == NULL) return;
90
91 /* initialize the entry */
92 pEntry->Usage = 1;
93 pEntry->Object = Object;
94 pEntry->Data = Data;
95
96 /* insert the entry into the cache */
97 pEntry->Next = U32AccelCache;
98 U32AccelCache = pEntry;
99 }
100
101 /* Create an accelerator table from a loaded resource */
102 HACCEL WINAPI U32LoadAccelerators(HINSTANCE hInstance, HRSRC hTableRes)
103 {
104 HGLOBAL hAccTableData;
105 HACCEL hAccTable = NULL;
106 U32_ACCEL_CACHE_ENTRY * pEntry;
107 RES_ACCEL * pAccTableResData;
108 RES_ACCEL * p;
109 SIZE_T i = 0;
110 SIZE_T j = 0;
111 ACCEL * pAccTableData;
112
113 /* load the accelerator table */
114 hAccTableData = LoadResource(hInstance, hTableRes);
115
116 /* failure */
117 if(hAccTableData == NULL) return NULL;
118
119 RtlEnterCriticalSection(&U32AccelCacheLock);
120
121 /* see if this accelerator table has already been loaded */
122 pEntry = *U32AccelCacheFind(NULL, hAccTableData);
123
124 /* accelerator table already loaded */
125 if(pEntry)
126 {
127 /* increment the reference count */
128 ++ pEntry->Usage;
129
130 /* return the existing object */
131 hAccTable = pEntry->Object;
132
133 /* success */
134 goto l_Leave;
135 }
136
137 /* count the number of entries in the table */
138 p = pAccTableResData = (RES_ACCEL *)hAccTableData;
139
140 while(1)
141 {
142 /* FIXME??? unknown flag 0x60 stops the scan */
143 if(p->fVirt & 0x60) break;
144
145 ++ i;
146 ++ p;
147
148 /* flag 0x80 marks the last entry of the table */
149 if(p->fVirt & 0x80) break;
150 }
151
152 /* allocate the buffer for the table to be passed to Win32K */
153 pAccTableData = LocalAlloc(LMEM_FIXED, i * sizeof(ACCEL));
154
155 /* failure */
156 if(pAccTableData == NULL) goto l_Leave;
157
158 /* copy the table */
159 for(j = 0; j < i; ++ j)
160 {
161 pAccTableData[j].fVirt = pAccTableResData[j].fVirt;
162 pAccTableData[j].key = pAccTableResData[j].key;
163 pAccTableData[j].cmd = pAccTableResData[j].cmd;
164 }
165
166 /* create a new accelerator table object */
167 hAccTable = NtUserCreateAcceleratorTable(pAccTableData, i);
168
169 /* free the buffer */
170 LocalFree(pAccTableData);
171
172 /* failure */
173 if(hAccTable == NULL) goto l_Leave;
174
175 /* success - cache the object */
176 U32AccelCacheAdd(hAccTable, pAccTableResData);
177
178 l_Leave:
179 RtlLeaveCriticalSection(&U32AccelCacheLock);
180 return hAccTable;
181 }
182
183 /* Checks if a message can be translated through an accelerator table */
184 BOOL WINAPI U32IsValidAccelMessage(UINT uMsg)
185 {
186 switch(uMsg)
187 {
188 case WM_KEYDOWN:
189 case WM_CHAR:
190 case WM_SYSKEYDOWN:
191 case WM_SYSCHAR:
192 return TRUE;
193
194 default:
195 return FALSE;
196 }
197 }
198
199 /* WIN32 FUNCTIONS ***********************************************************/
200
201 /*
202 * Dereference the specified accelerator table, removing it from the cache and
203 * deleting the associated NtUser object as appropriate
204 *
205 * @implemented
206 */
207 BOOL WINAPI DestroyAcceleratorTable(HACCEL hAccel)
208 {
209 U32_ACCEL_CACHE_ENTRY ** ppEntry;
210 ULONG_PTR nUsage = 0;
211
212 RtlEnterCriticalSection(&U32AccelCacheLock);
213
214 /* see if this accelerator table has been cached */
215 ppEntry = U32AccelCacheFind(hAccel, NULL);
216
217 /* accelerator table cached */
218 if(*ppEntry)
219 {
220 U32_ACCEL_CACHE_ENTRY * pEntry = *ppEntry;
221
222 /* decrement the reference count */
223 nUsage = pEntry->Usage= pEntry->Usage - 1;
224
225 /* reference count now zero: destroy the cache entry */
226 if(nUsage == 0)
227 {
228 /* unlink the cache entry */
229 *ppEntry = pEntry->Next;
230
231 /* free the cache entry */
232 LocalFree(pEntry);
233 }
234 }
235
236 RtlLeaveCriticalSection(&U32AccelCacheLock);
237
238 if(nUsage > 0) return FALSE;
239
240 /* destroy the object */
241 return NtUserDestroyAcceleratorTable(hAccel);
242 }
243
244
245 /*
246 * Create an accelerator table from a named resource
247 *
248 * @implemented
249 */
250 HACCEL WINAPI LoadAcceleratorsW(HINSTANCE hInstance, LPCWSTR lpTableName)
251 {
252 return U32LoadAccelerators
253 (
254 hInstance,
255 FindResourceExW(hInstance, MAKEINTRESOURCEW(RT_ACCELERATOR), lpTableName, 0)
256 );
257 }
258
259
260 /*
261 * @implemented
262 */
263 HACCEL WINAPI LoadAcceleratorsA(HINSTANCE hInstance, LPCSTR lpTableName)
264 {
265 return U32LoadAccelerators
266 (
267 hInstance,
268 FindResourceExA(hInstance, MAKEINTRESOURCEA(RT_ACCELERATOR), lpTableName, 0)
269 );
270 }
271
272 /*
273 * Translate a key press into a WM_COMMAND message
274 *
275 * @implemented
276 */
277 int WINAPI TranslateAcceleratorW(HWND hWnd, HACCEL hAccTable, LPMSG lpMsg)
278 {
279 if(!U32IsValidAccelMessage(lpMsg->message)) return 0;
280
281 return NtUserTranslateAccelerator(hWnd, hAccTable, lpMsg);
282 }
283
284 /*
285 * @implemented
286 */
287 int WINAPI CopyAcceleratorTableW
288 (
289 HACCEL hAccelSrc,
290 LPACCEL lpAccelDst,
291 int cAccelEntries
292 )
293 {
294 return NtUserCopyAcceleratorTable(hAccelSrc, lpAccelDst, cAccelEntries);
295 }
296
297 /*
298 * @implemented
299 */
300 HACCEL WINAPI CreateAcceleratorTableW(LPACCEL lpaccl, int cEntries)
301 {
302 return NtUserCreateAcceleratorTable(lpaccl, cEntries);
303 }
304
305
306 /*
307 * @implemented
308 */
309 int WINAPI CopyAcceleratorTableA
310 (
311 HACCEL hAccelSrc,
312 LPACCEL lpAccelDst,
313 int cAccelEntries
314 )
315 {
316 int i;
317
318 cAccelEntries = CopyAcceleratorTableW(hAccelSrc, lpAccelDst, cAccelEntries);
319
320 if(cAccelEntries == 0) return 0;
321
322 for(i = 0; i < cAccelEntries; ++ i)
323 if(!(lpAccelDst[i].fVirt & FVIRTKEY))
324 {
325 NTSTATUS nErrCode = RtlUnicodeToMultiByteN
326 (
327 (PCHAR)&lpAccelDst[i].key,
328 sizeof(lpAccelDst[i].key),
329 NULL,
330 (PWCHAR)&lpAccelDst[i].key,
331 sizeof(lpAccelDst[i].key)
332 );
333
334 if(!NT_SUCCESS(nErrCode)) lpAccelDst[i].key = 0;
335 }
336
337 return cAccelEntries;
338 }
339
340
341 /*
342 * @implemented
343 */
344 HACCEL WINAPI CreateAcceleratorTableA(LPACCEL lpaccl, int cEntries)
345 {
346 int i;
347
348 for(i = 0; i < cEntries; ++ i)
349 if(!lpaccl[i].fVirt)
350 {
351 NTSTATUS nErrCode = RtlMultiByteToUnicodeN
352 (
353 (PWCHAR)&lpaccl[i].key,
354 sizeof(lpaccl[i].key),
355 NULL,
356 (PCHAR)&lpaccl[i].key,
357 sizeof(lpaccl[i].key)
358 );
359
360 if(!NT_SUCCESS(nErrCode)) lpaccl[i].key = -1;
361 }
362
363 return CreateAcceleratorTableW(lpaccl, cEntries);
364 }
365
366
367 /*
368 * @implemented
369 */
370 int WINAPI TranslateAcceleratorA(HWND hWnd, HACCEL hAccTable, LPMSG lpMsg)
371 {
372 MSG mCopy = *lpMsg;
373 CHAR cChar;
374 WCHAR wChar;
375
376 if(!U32IsValidAccelMessage(lpMsg->message)) return 0;
377
378 NTSTATUS nErrCode =
379 RtlMultiByteToUnicodeN(&wChar, sizeof(wChar), NULL, &cChar, sizeof(cChar));
380
381 if(!nErrCode)
382 {
383 SetLastError(RtlNtStatusToDosError(nErrCode));
384 return 0;
385 }
386
387 return TranslateAcceleratorW(hWnd, hAccTable, &mCopy);
388 }
389
390 /* EOF */