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