xref: /reactos/win32ss/user/ntuser/accelerator.c (revision 90ed6862)
1 /*
2  * COPYRIGHT:        See COPYING in the top level directory
3  * PROJECT:          ReactOS kernel
4  * PURPOSE:          Window accelerator
5  * FILE:             win32ss/user/ntuser/accelerator.c
6  * PROGRAMER:        Casper S. Hornstrup (chorns@users.sourceforge.net)
7  *                   Copyright 1993 Martin Ayotte
8  *                   Copyright 1994 Alexandre Julliard
9  *                   Copyright 1997 Morten Welinder
10  *                   Copyright 2011 Rafal Harabien
11  */
12 
13 #include <win32k.h>
14 DBG_DEFAULT_CHANNEL(UserAccel);
15 
16 #define FVIRT_TBL_END 0x80
17 #define FVIRT_MASK 0x7F
18 
19 /* FUNCTIONS *****************************************************************/
20 
UserGetAccelObject(HACCEL hAccel)21 PACCELERATOR_TABLE FASTCALL UserGetAccelObject(HACCEL hAccel)
22 {
23     PACCELERATOR_TABLE Accel;
24 
25     if (!hAccel)
26     {
27         EngSetLastError(ERROR_INVALID_ACCEL_HANDLE);
28         return NULL;
29     }
30 
31     Accel = UserGetObject(gHandleTable, hAccel, TYPE_ACCELTABLE);
32     if (!Accel)
33     {
34         EngSetLastError(ERROR_INVALID_ACCEL_HANDLE);
35         return NULL;
36     }
37 
38     return Accel;
39 }
40 
41 
42 static
43 BOOLEAN FASTCALL
co_IntTranslateAccelerator(PWND Window,CONST MSG * pMsg,CONST ACCEL * pAccel)44 co_IntTranslateAccelerator(
45     PWND Window,
46     CONST MSG *pMsg,
47     CONST ACCEL *pAccel)
48 {
49     BOOL bFound = FALSE;
50     UINT Mask = 0, nPos;
51     HWND hWnd;
52     HMENU hMenu, hSubMenu;
53     PMENU MenuObject;
54 
55     ASSERT_REFS_CO(Window);
56 
57     hWnd = UserHMGetHandle(Window);
58 
59     TRACE("IntTranslateAccelerator(hwnd %p, message %x, wParam %x, lParam %x, fVirt 0x%x, key %x, cmd %x)\n",
60           hWnd, pMsg->message, pMsg->wParam, pMsg->lParam, pAccel->fVirt, pAccel->key, pAccel->cmd);
61 
62     if (UserGetKeyState(VK_CONTROL) & 0x8000) Mask |= FCONTROL;
63     if (UserGetKeyState(VK_MENU) & 0x8000) Mask |= FALT; // FIXME: VK_LMENU (msg winetest!)
64     if (UserGetKeyState(VK_SHIFT) & 0x8000) Mask |= FSHIFT;
65     TRACE("Mask 0x%x\n", Mask);
66 
67     if (pAccel->fVirt & FVIRTKEY)
68     {
69         /* This is a virtual key. Process WM_(SYS)KEYDOWN messages. */
70         if (pMsg->message == WM_KEYDOWN || pMsg->message == WM_SYSKEYDOWN)
71         {
72             /* Check virtual key and SHIFT, CTRL, LALT state */
73             if (pMsg->wParam == pAccel->key && Mask == (pAccel->fVirt & (FSHIFT | FCONTROL | FALT)))
74             {
75                 bFound = TRUE;
76             }
77         }
78     }
79     else
80     {
81         /* This is a char code. Process WM_(SYS)CHAR messages. */
82         if (pMsg->message == WM_CHAR || pMsg->message == WM_SYSCHAR)
83         {
84             /* Check char code and LALT state only */
85             if (pMsg->wParam == pAccel->key && (Mask & FALT) == (pAccel->fVirt & FALT))
86             {
87                 bFound = TRUE;
88             }
89         }
90     }
91 
92     if (!bFound)
93     {
94         /* Don't translate this msg */
95         TRACE("IntTranslateAccelerator returns FALSE\n");
96         return FALSE;
97     }
98 
99     /* Check if accelerator is associated with menu command */
100     hMenu = (Window->style & WS_CHILD) ? 0 : (HMENU)Window->IDMenu;
101     hSubMenu = NULL;
102     MenuObject = UserGetMenuObject(hMenu);
103     nPos = pAccel->cmd;
104     if (MenuObject)
105     {
106         if ((MENU_FindItem (&MenuObject, &nPos, MF_BYPOSITION)))
107             hSubMenu = UserHMGetHandle(MenuObject);
108         else
109             hMenu = NULL;
110     }
111     if (!hMenu)
112     {
113         /* Check system menu now */
114         hMenu = Window->SystemMenu;
115         hSubMenu = hMenu; /* system menu is a popup menu */
116         MenuObject = UserGetMenuObject(hMenu);
117         nPos = pAccel->cmd;
118         if (MenuObject)
119         {
120             if ((MENU_FindItem (&MenuObject, &nPos, MF_BYPOSITION)))
121                 hSubMenu = UserHMGetHandle(MenuObject);
122             else
123                 hMenu = NULL;
124         }
125     }
126 
127     /* If this is a menu item, there is no capturing enabled and
128        window is not disabled, send WM_INITMENU */
129     if (hMenu && !IntGetCaptureWindow())
130     {
131         co_IntSendMessage(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
132         if (hSubMenu)
133         {
134             nPos = IntFindSubMenu(&hMenu, hSubMenu);
135             TRACE("hSysMenu = %p, hSubMenu = %p, nPos = %u\n", hMenu, hSubMenu, nPos);
136             co_IntSendMessage(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
137         }
138     }
139 
140     /* Don't send any message if:
141        - window is disabled
142        - menu item is disabled
143        - this is window menu and window is minimized */
144     if (!(Window->style & WS_DISABLED) &&
145             !(hMenu && IntGetMenuState(hMenu, pAccel->cmd, MF_BYCOMMAND) & (MF_DISABLED | MF_GRAYED)) &&
146             !(hMenu && hMenu == (HMENU)Window->IDMenu && (Window->style & WS_MINIMIZED)))
147     {
148         /* If this is system menu item, send WM_SYSCOMMAND, otherwise send WM_COMMAND */
149         if (hMenu && hMenu == Window->SystemMenu)
150         {
151             TRACE("Sending WM_SYSCOMMAND, wParam=%0x\n", pAccel->cmd);
152             co_IntSendMessage(hWnd, WM_SYSCOMMAND, pAccel->cmd, 0x00010000L);
153         }
154         else
155         {
156             TRACE("Sending WM_COMMAND, wParam=%0x\n", 0x10000 | pAccel->cmd);
157             co_IntSendMessage(hWnd, WM_COMMAND, 0x10000 | pAccel->cmd, 0L);
158         }
159     }
160 
161     TRACE("IntTranslateAccelerator returns TRUE\n");
162     return TRUE;
163 }
164 
165 
166 /* SYSCALLS *****************************************************************/
167 
168 
169 ULONG
170 APIENTRY
NtUserCopyAcceleratorTable(HACCEL hAccel,LPACCEL Entries,ULONG EntriesCount)171 NtUserCopyAcceleratorTable(
172     HACCEL hAccel,
173     LPACCEL Entries,
174     ULONG EntriesCount)
175 {
176     PACCELERATOR_TABLE Accel;
177     ULONG Ret = 0;
178 
179     TRACE("Enter NtUserCopyAcceleratorTable\n");
180     UserEnterShared();
181 
182     Accel = UserGetAccelObject(hAccel);
183     if (!Accel)
184     {
185         goto Exit; // Return 0
186     }
187 
188     /* If Entries is NULL return table size */
189     if (!Entries)
190     {
191         Ret = Accel->Count;
192         goto Exit;
193     }
194 
195     /* Don't overrun */
196     if (Accel->Count < EntriesCount)
197         EntriesCount = Accel->Count;
198 
199     _SEH2_TRY
200     {
201         ProbeForWrite(Entries, EntriesCount*sizeof(Entries[0]), 4);
202 
203         for (Ret = 0; Ret < EntriesCount; Ret++)
204         {
205             Entries[Ret].fVirt = Accel->Table[Ret].fVirt;
206             Entries[Ret].key = Accel->Table[Ret].key;
207             Entries[Ret].cmd = Accel->Table[Ret].cmd;
208         }
209     }
210     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
211     {
212         SetLastNtError(_SEH2_GetExceptionCode());
213         Ret = 0;
214     }
215     _SEH2_END;
216 
217 Exit:
218     TRACE("Leave NtUserCopyAcceleratorTable, ret=%lu\n", Ret);
219     UserLeave();
220     return Ret;
221 }
222 
223 HACCEL
224 APIENTRY
NtUserCreateAcceleratorTable(LPACCEL Entries,ULONG EntriesCount)225 NtUserCreateAcceleratorTable(
226     LPACCEL Entries,
227     ULONG EntriesCount)
228 {
229     PACCELERATOR_TABLE Accel;
230     HACCEL hAccel;
231     ULONG Index;
232     NTSTATUS Status = STATUS_SUCCESS;
233     HACCEL Ret = NULL;
234     PTHREADINFO pti;
235 
236     TRACE("Enter NtUserCreateAcceleratorTable(Entries %p, EntriesCount %u)\n",
237           Entries, EntriesCount);
238     UserEnterExclusive();
239 
240     if (!Entries || EntriesCount <= 0)
241     {
242         SetLastNtError(STATUS_INVALID_PARAMETER);
243         goto Exit; // Return NULL
244     }
245 
246     pti = PsGetCurrentThreadWin32Thread();
247 
248     Accel = UserCreateObject(gHandleTable,
249         pti->rpdesk,
250         pti,
251         (PHANDLE)&hAccel,
252         TYPE_ACCELTABLE,
253         sizeof(ACCELERATOR_TABLE));
254 
255     if (Accel == NULL)
256     {
257         SetLastNtError(STATUS_NO_MEMORY);
258         goto Exit; // Return NULL
259     }
260 
261     Accel->Count = EntriesCount;
262     Accel->Table = ExAllocatePoolWithTag(PagedPool, EntriesCount * sizeof(ACCEL), USERTAG_ACCEL);
263     if (Accel->Table == NULL)
264     {
265         UserDereferenceObject(Accel);
266         UserDeleteObject(hAccel, TYPE_ACCELTABLE);
267         SetLastNtError(STATUS_NO_MEMORY);
268         goto Exit; // Return NULL
269     }
270 
271     _SEH2_TRY
272     {
273         ProbeForRead(Entries, EntriesCount * sizeof(ACCEL), 4);
274 
275         for (Index = 0; Index < EntriesCount; Index++)
276         {
277             Accel->Table[Index].fVirt = Entries[Index].fVirt & FVIRT_MASK;
278             if(Accel->Table[Index].fVirt & FVIRTKEY)
279             {
280                 Accel->Table[Index].key = Entries[Index].key;
281             }
282             else
283             {
284                 RtlMultiByteToUnicodeN(&Accel->Table[Index].key,
285                 sizeof(WCHAR),
286                 NULL,
287                 (PCSTR)&Entries[Index].key,
288                 sizeof(CHAR));
289             }
290 
291             Accel->Table[Index].cmd = Entries[Index].cmd;
292         }
293     }
294     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
295     {
296         Status = _SEH2_GetExceptionCode();
297     }
298     _SEH2_END;
299 
300     if (!NT_SUCCESS(Status))
301     {
302         ExFreePoolWithTag(Accel->Table, USERTAG_ACCEL);
303         UserDereferenceObject(Accel);
304         UserDeleteObject(hAccel, TYPE_ACCELTABLE);
305         SetLastNtError(Status);
306         goto Exit; // Return NULL
307     }
308 
309     /* FIXME: Save HandleTable in a list somewhere so we can clean it up again */
310 
311     /* Release the extra reference (UserCreateObject added 2 references) */
312     UserDereferenceObject(Accel);
313 
314     Ret = hAccel;
315 
316 Exit:
317     TRACE("Leave NtUserCreateAcceleratorTable(Entries %p, EntriesCount %u) = %p\n",
318           Entries, EntriesCount, Ret);
319     UserLeave();
320     return Ret;
321 }
322 
323 BOOLEAN
UserDestroyAccelTable(PVOID Object)324 UserDestroyAccelTable(PVOID Object)
325 {
326     PACCELERATOR_TABLE Accel = Object;
327 
328     if (Accel->Table != NULL)
329     {
330         ExFreePoolWithTag(Accel->Table, USERTAG_ACCEL);
331         Accel->Table = NULL;
332     }
333 
334     UserDeleteObject(UserHMGetHandle(Accel), TYPE_ACCELTABLE);
335     return TRUE;
336 }
337 
338 BOOLEAN
339 APIENTRY
NtUserDestroyAcceleratorTable(HACCEL hAccel)340 NtUserDestroyAcceleratorTable(
341     HACCEL hAccel)
342 {
343     PACCELERATOR_TABLE Accel;
344     BOOLEAN Ret = FALSE;
345 
346     /* FIXME: If the handle table is from a call to LoadAcceleratorTable, decrement it's
347        usage count (and return TRUE).
348     FIXME: Destroy only tables created using CreateAcceleratorTable.
349      */
350 
351     TRACE("NtUserDestroyAcceleratorTable(Table %p)\n", hAccel);
352     UserEnterExclusive();
353 
354     Accel = UserGetAccelObject(hAccel);
355     if (Accel)
356     {
357         Ret = UserDestroyAccelTable(Accel);
358     }
359 
360     TRACE("Leave NtUserDestroyAcceleratorTable(Table %p) = %u\n", hAccel, Ret);
361     UserLeave();
362     return Ret;
363 }
364 
365 int
366 APIENTRY
NtUserTranslateAccelerator(HWND hWnd,HACCEL hAccel,LPMSG pUnsafeMessage)367 NtUserTranslateAccelerator(
368     HWND hWnd,
369     HACCEL hAccel,
370     LPMSG pUnsafeMessage)
371 {
372     PWND Window = NULL;
373     PACCELERATOR_TABLE Accel = NULL;
374     ULONG i;
375     MSG Message;
376     USER_REFERENCE_ENTRY AccelRef, WindowRef;
377     int Ret = 0;
378 
379     TRACE("NtUserTranslateAccelerator(hWnd %p, hAccel %p, Message %p)\n",
380           hWnd, hAccel, pUnsafeMessage);
381     UserEnterShared();
382 
383     if (hWnd == NULL)
384     {
385         goto Cleanup; // Return 0
386     }
387 
388     _SEH2_TRY
389     {
390         ProbeForRead(pUnsafeMessage, sizeof(MSG), 4);
391         RtlCopyMemory(&Message, pUnsafeMessage, sizeof(MSG));
392     }
393     _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
394     {
395         SetLastNtError(_SEH2_GetExceptionCode());
396         _SEH2_YIELD(goto Cleanup); // Return 0
397     }
398     _SEH2_END;
399 
400     if ((Message.message != WM_KEYDOWN) &&
401         (Message.message != WM_SYSKEYDOWN) &&
402         (Message.message != WM_SYSCHAR) &&
403         (Message.message != WM_CHAR))
404     {
405         goto Cleanup; // Return 0
406     }
407 
408     Accel = UserGetAccelObject(hAccel);
409     if (!Accel)
410     {
411         goto Cleanup; // Return 0
412     }
413 
414     UserRefObjectCo(Accel, &AccelRef);
415 
416     Window = UserGetWindowObject(hWnd);
417     if (!Window)
418     {
419         goto Cleanup; // Return 0
420     }
421 
422     UserRefObjectCo(Window, &WindowRef);
423 
424     /* FIXME: Associate AcceleratorTable with the current thread */
425 
426     for (i = 0; i < Accel->Count; i++)
427     {
428         if (co_IntTranslateAccelerator(Window, &Message, &Accel->Table[i]))
429         {
430             Ret = 1;
431             break;
432         }
433 
434         /* Undocumented feature... */
435         if (Accel->Table[i].fVirt & FVIRT_TBL_END)
436             break;
437     }
438 
439 Cleanup:
440     if (Window) UserDerefObjectCo(Window);
441     if (Accel) UserDerefObjectCo(Accel);
442 
443     TRACE("NtUserTranslateAccelerator returns %d\n", Ret);
444     UserLeave();
445     return Ret;
446 }
447