xref: /reactos/win32ss/user/user32/windows/accel.c (revision 2196a06f)
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 */
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 */
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 */
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 */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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