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