[WIN32SS][FONT] Fix font metrics (#713)
[reactos.git] / win32ss / user / ntuser / accelerator.c
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
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
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 = Window->head.h;
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 = MenuObject->head.h;
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 = MenuObject->head.h;
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
171 NtUserCopyAcceleratorTable(
172 HACCEL hAccel,
173 LPACCEL Entries,
174 ULONG EntriesCount)
175 {
176 PACCELERATOR_TABLE Accel;
177 ULONG Ret;
178 DECLARE_RETURN(int);
179
180 TRACE("Enter NtUserCopyAcceleratorTable\n");
181 UserEnterShared();
182
183 Accel = UserGetAccelObject(hAccel);
184 if (!Accel)
185 {
186 RETURN(0);
187 }
188
189 /* If Entries is NULL return table size */
190 if (!Entries)
191 {
192 RETURN(Accel->Count);
193 }
194
195 /* Don't overrun */
196 if (Accel->Count < EntriesCount)
197 EntriesCount = Accel->Count;
198
199 Ret = 0;
200
201 _SEH2_TRY
202 {
203 ProbeForWrite(Entries, EntriesCount*sizeof(Entries[0]), 4);
204
205 for (Ret = 0; Ret < EntriesCount; Ret++)
206 {
207 Entries[Ret].fVirt = Accel->Table[Ret].fVirt;
208 Entries[Ret].key = Accel->Table[Ret].key;
209 Entries[Ret].cmd = Accel->Table[Ret].cmd;
210 }
211 }
212 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
213 {
214 SetLastNtError(_SEH2_GetExceptionCode());
215 Ret = 0;
216 }
217 _SEH2_END;
218
219 RETURN(Ret);
220
221 CLEANUP:
222 TRACE("Leave NtUserCopyAcceleratorTable, ret=%i\n", _ret_);
223 UserLeave();
224 END_CLEANUP;
225 }
226
227 HACCEL
228 APIENTRY
229 NtUserCreateAcceleratorTable(
230 LPACCEL Entries,
231 ULONG EntriesCount)
232 {
233 PACCELERATOR_TABLE Accel;
234 HACCEL hAccel;
235 ULONG Index;
236 NTSTATUS Status = STATUS_SUCCESS;
237 DECLARE_RETURN(HACCEL);
238 PTHREADINFO pti;
239
240 TRACE("Enter NtUserCreateAcceleratorTable(Entries %p, EntriesCount %u)\n",
241 Entries, EntriesCount);
242 UserEnterExclusive();
243
244 if (!Entries || EntriesCount <= 0)
245 {
246 SetLastNtError(STATUS_INVALID_PARAMETER);
247 RETURN( (HACCEL) NULL );
248 }
249
250 pti = PsGetCurrentThreadWin32Thread();
251
252 Accel = UserCreateObject(gHandleTable,
253 pti->rpdesk,
254 pti,
255 (PHANDLE)&hAccel,
256 TYPE_ACCELTABLE,
257 sizeof(ACCELERATOR_TABLE));
258
259 if (Accel == NULL)
260 {
261 SetLastNtError(STATUS_NO_MEMORY);
262 RETURN( (HACCEL) NULL );
263 }
264
265 Accel->Count = EntriesCount;
266 Accel->Table = ExAllocatePoolWithTag(PagedPool, EntriesCount * sizeof(ACCEL), USERTAG_ACCEL);
267 if (Accel->Table == NULL)
268 {
269 UserDereferenceObject(Accel);
270 UserDeleteObject(hAccel, TYPE_ACCELTABLE);
271 SetLastNtError(STATUS_NO_MEMORY);
272 RETURN( (HACCEL) NULL);
273 }
274
275 _SEH2_TRY
276 {
277 ProbeForRead(Entries, EntriesCount * sizeof(ACCEL), 4);
278
279 for (Index = 0; Index < EntriesCount; Index++)
280 {
281 Accel->Table[Index].fVirt = Entries[Index].fVirt & FVIRT_MASK;
282 if(Accel->Table[Index].fVirt & FVIRTKEY)
283 {
284 Accel->Table[Index].key = Entries[Index].key;
285 }
286 else
287 {
288 RtlMultiByteToUnicodeN(&Accel->Table[Index].key,
289 sizeof(WCHAR),
290 NULL,
291 (PCSTR)&Entries[Index].key,
292 sizeof(CHAR));
293 }
294
295 Accel->Table[Index].cmd = Entries[Index].cmd;
296 }
297 }
298 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
299 {
300 Status = _SEH2_GetExceptionCode();
301 }
302 _SEH2_END;
303
304 if (!NT_SUCCESS(Status))
305 {
306 ExFreePoolWithTag(Accel->Table, USERTAG_ACCEL);
307 UserDereferenceObject(Accel);
308 UserDeleteObject(hAccel, TYPE_ACCELTABLE);
309 SetLastNtError(Status);
310 RETURN( (HACCEL) NULL);
311 }
312
313 /* FIXME: Save HandleTable in a list somewhere so we can clean it up again */
314
315 /* Release the extra reference (UserCreateObject added 2 references) */
316 UserDereferenceObject(Accel);
317
318 RETURN(hAccel);
319
320 CLEANUP:
321 TRACE("Leave NtUserCreateAcceleratorTable(Entries %p, EntriesCount %u) = %p\n",
322 Entries, EntriesCount, _ret_);
323 UserLeave();
324 END_CLEANUP;
325 }
326
327 BOOLEAN
328 UserDestroyAccelTable(PVOID Object)
329 {
330 PACCELERATOR_TABLE Accel = Object;
331
332 if (Accel->Table != NULL)
333 {
334 ExFreePoolWithTag(Accel->Table, USERTAG_ACCEL);
335 Accel->Table = NULL;
336 }
337
338 UserDeleteObject(Accel->head.h, TYPE_ACCELTABLE);
339 return TRUE;
340 }
341
342 BOOLEAN
343 APIENTRY
344 NtUserDestroyAcceleratorTable(
345 HACCEL hAccel)
346 {
347 PACCELERATOR_TABLE Accel;
348 DECLARE_RETURN(BOOLEAN);
349
350 /* FIXME: If the handle table is from a call to LoadAcceleratorTable, decrement it's
351 usage count (and return TRUE).
352 FIXME: Destroy only tables created using CreateAcceleratorTable.
353 */
354
355 TRACE("NtUserDestroyAcceleratorTable(Table %p)\n", hAccel);
356 UserEnterExclusive();
357
358 if (!(Accel = UserGetAccelObject(hAccel)))
359 {
360 RETURN( FALSE);
361 }
362
363 UserDestroyAccelTable(Accel);
364
365 RETURN( TRUE);
366
367 CLEANUP:
368 TRACE("Leave NtUserDestroyAcceleratorTable(Table %p) = %u\n", hAccel, _ret_);
369 UserLeave();
370 END_CLEANUP;
371 }
372
373 int
374 APIENTRY
375 NtUserTranslateAccelerator(
376 HWND hWnd,
377 HACCEL hAccel,
378 LPMSG pUnsafeMessage)
379 {
380 PWND Window = NULL;
381 PACCELERATOR_TABLE Accel = NULL;
382 ULONG i;
383 MSG Message;
384 USER_REFERENCE_ENTRY AccelRef, WindowRef;
385 DECLARE_RETURN(int);
386
387 TRACE("NtUserTranslateAccelerator(hWnd %p, hAccel %p, Message %p)\n",
388 hWnd, hAccel, pUnsafeMessage);
389 UserEnterShared();
390
391 if (hWnd == NULL)
392 {
393 RETURN( 0);
394 }
395
396 _SEH2_TRY
397 {
398 ProbeForRead(pUnsafeMessage, sizeof(MSG), 4);
399 RtlCopyMemory(&Message, pUnsafeMessage, sizeof(MSG));
400 }
401 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
402 {
403 SetLastNtError(_SEH2_GetExceptionCode());
404 _SEH2_YIELD(RETURN( 0));
405 }
406 _SEH2_END;
407
408 if ((Message.message != WM_KEYDOWN) &&
409 (Message.message != WM_SYSKEYDOWN) &&
410 (Message.message != WM_SYSCHAR) &&
411 (Message.message != WM_CHAR))
412 {
413 RETURN( 0);
414 }
415
416 Accel = UserGetAccelObject(hAccel);
417 if (!Accel)
418 {
419 RETURN( 0);
420 }
421
422 UserRefObjectCo(Accel, &AccelRef);
423
424 Window = UserGetWindowObject(hWnd);
425 if (!Window)
426 {
427 RETURN( 0);
428 }
429
430 UserRefObjectCo(Window, &WindowRef);
431
432 /* FIXME: Associate AcceleratorTable with the current thread */
433
434 for (i = 0; i < Accel->Count; i++)
435 {
436 if (co_IntTranslateAccelerator(Window, &Message, &Accel->Table[i]))
437 {
438 RETURN( 1);
439 }
440
441 /* Undocumented feature... */
442 if (Accel->Table[i].fVirt & FVIRT_TBL_END)
443 break;
444 }
445
446 RETURN( 0);
447
448 CLEANUP:
449 if (Window) UserDerefObjectCo(Window);
450 if (Accel) UserDerefObjectCo(Accel);
451
452 TRACE("NtUserTranslateAccelerator returns %d\n", _ret_);
453 UserLeave();
454 END_CLEANUP;
455 }