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