[WIN32K]
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / accelerator.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window accelerator
5 * FILE: subsystems/win32/win32k/ntuser/accelerator.c
6 * PROGRAMER: Casper S. Hornstrup (chorns@users.sourceforge.net)
7 * REVISION HISTORY:
8 * 06-06-2001 CSH Created
9 */
10
11 /*
12 * Copyright 1993 Martin Ayotte
13 * Copyright 1994 Alexandre Julliard
14 * Copyright 1997 Morten Welinder
15 *
16 * This library is free software; you can redistribute it and/or
17 * modify it under the terms of the GNU Lesser General Public
18 * License as published by the Free Software Foundation; either
19 * version 2.1 of the License, or (at your option) any later version.
20 *
21 * This library is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
24 * Lesser General Public License for more details.
25 *
26 * You should have received a copy of the GNU Lesser General Public
27 * License along with this library; if not, write to the Free Software
28 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
29 */
30
31 /* INCLUDES ******************************************************************/
32
33 #include <win32k.h>
34
35 DBG_DEFAULT_CHANNEL(UserAccel);
36
37 UINT FASTCALL IntFindSubMenu(HMENU *hMenu, HMENU hSubTarget );
38 HMENU FASTCALL IntGetSubMenu( HMENU hMenu, int nPos);
39 UINT FASTCALL IntGetMenuState( HMENU hMenu, UINT uId, UINT uFlags);
40
41 /* FUNCTIONS *****************************************************************/
42
43 PACCELERATOR_TABLE FASTCALL UserGetAccelObject(HACCEL hAccel)
44 {
45 PACCELERATOR_TABLE Accel;
46
47 if (!hAccel)
48 {
49 EngSetLastError(ERROR_INVALID_ACCEL_HANDLE);
50 return NULL;
51 }
52
53 Accel= UserGetObject(gHandleTable, hAccel, otAccel);
54 if (!Accel)
55 {
56 EngSetLastError(ERROR_INVALID_ACCEL_HANDLE);
57 return NULL;
58 }
59
60 return Accel;
61 }
62
63
64 static
65 BOOLEAN FASTCALL
66 co_IntTranslateAccelerator(
67 PWND Window,
68 UINT message,
69 WPARAM wParam,
70 LPARAM lParam,
71 BYTE fVirt,
72 WORD key,
73 WORD cmd)
74 {
75 INT mask = 0;
76 UINT mesg = 0;
77 HWND hWnd;
78
79 ASSERT_REFS_CO(Window);
80
81 hWnd = Window->head.h;
82
83 TRACE("IntTranslateAccelerator(hwnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x)\n",
84 Window->head.h, message, wParam, lParam, fVirt, key, cmd);
85
86 if (wParam != key)
87 {
88 return FALSE;
89 }
90
91 TRACE("NtUserGetKeyState(VK_CONTROL) = 0x%x\n",UserGetKeyState(VK_CONTROL));
92 TRACE("NtUserGetKeyState(VK_MENU) = 0x%x\n",UserGetKeyState(VK_MENU));
93 TRACE("NtUserGetKeyState(VK_SHIFT) = 0x%x\n",UserGetKeyState(VK_SHIFT));
94
95 if (UserGetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
96 if (UserGetKeyState(VK_MENU) & 0x8000) mask |= FALT;
97 if (UserGetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
98 TRACE("Mask 0x%x\n",mask);
99
100 if (message == WM_CHAR || message == WM_SYSCHAR)
101 {
102 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
103 {
104 TRACE("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
105 goto found;
106 }
107 }
108 else
109 {
110 if (fVirt & FVIRTKEY)
111 {
112 TRACE("found accel for virt_key %04x (scan %04x)\n",
113 wParam, 0xff & HIWORD(lParam));
114
115 if (mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
116 TRACE("but incorrect SHIFT/CTRL/ALT-state mask %x fVirt %x\n",mask,fVirt);
117 }
118 else
119 {
120 if (!(lParam & 0x01000000)) /* no special_key */
121 {
122 if ((fVirt & FALT) && (lParam & 0x20000000))
123 { /* ^^ ALT pressed */
124 TRACE("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
125 goto found;
126 }
127 }
128 }
129 }
130
131 TRACE("IntTranslateAccelerator(hwnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x) = FALSE\n",
132 Window->head.h, message, wParam, lParam, fVirt, key, cmd);
133
134 return FALSE;
135
136 found:
137 if (message == WM_KEYUP || message == WM_SYSKEYUP)
138 mesg = 1;
139 else
140 {
141 HMENU hMenu, hSubMenu, hSysMenu;
142 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
143 PMENU_OBJECT MenuObject, SubMenu;
144 PMENU_ITEM MenuItem;
145
146 hMenu = (Window->style & WS_CHILD) ? 0 : (HMENU)Window->IDMenu;
147 hSysMenu = Window->SystemMenu;
148 MenuObject = IntGetMenuObject(Window->SystemMenu);
149
150 /* find menu item and ask application to initialize it */
151 /* 1. in the system menu */
152 hSubMenu = hSysMenu;
153 if (MenuObject)
154 {
155 nPos = IntGetMenuItemByFlag( MenuObject,
156 cmd,
157 MF_BYCOMMAND,
158 &SubMenu,
159 &MenuItem,
160 NULL);
161
162 if (MenuItem && (nPos != (UINT)-1))
163 {
164 hSubMenu = MenuItem->hSubMenu;
165
166 if (IntGetCaptureWindow())
167 mesg = 2;
168 if (Window->style & WS_DISABLED)
169 mesg = 3;
170 else
171 {
172 co_IntSendMessage(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
173 if (hSubMenu != hSysMenu)
174 {
175 nPos = IntFindSubMenu(&hSysMenu, hSubMenu);
176 TRACE("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
177 co_IntSendMessage(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
178 }
179 uSysStat = IntGetMenuState(IntGetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
180 }
181 }
182 else /* 2. in the window's menu */
183 {
184 MenuObject = IntGetMenuObject(hMenu);
185 hSubMenu = hMenu;
186 if (MenuObject)
187 {
188 nPos = IntGetMenuItemByFlag( MenuObject,
189 cmd,
190 MF_BYCOMMAND,
191 &SubMenu,
192 &MenuItem,
193 NULL);
194
195 if (MenuItem && (nPos != (UINT)-1))
196 {
197 if (IntGetCaptureWindow())
198 mesg = 2;
199 if (Window->style & WS_DISABLED)
200 mesg = 3;
201 else
202 {
203 co_IntSendMessage(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
204 if (hSubMenu != hMenu)
205 {
206 nPos = IntFindSubMenu(&hMenu, hSubMenu);
207 TRACE("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
208 co_IntSendMessage(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
209 }
210 uStat = IntGetMenuState(hMenu, cmd, MF_BYCOMMAND);
211 }
212 }
213 }
214 }
215 }
216 if (mesg == 0)
217 {
218 if (uSysStat != (UINT)-1)
219 {
220 if (uSysStat & (MF_DISABLED|MF_GRAYED))
221 mesg=4;
222 else
223 mesg=WM_SYSCOMMAND;
224 }
225 else
226 {
227 if (uStat != (UINT)-1)
228 {
229 if (Window->style & WS_MINIMIZE)
230 mesg=5;
231 else
232 {
233 if (uStat & (MF_DISABLED|MF_GRAYED))
234 mesg=6;
235 else
236 mesg=WM_COMMAND;
237 }
238 }
239 else
240 {
241 mesg=WM_COMMAND;
242 }
243 }
244 }
245 }
246
247 if (mesg == WM_COMMAND)
248 {
249 TRACE(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
250 co_IntSendMessage(Window->head.h, mesg, 0x10000 | cmd, 0L);
251 }
252 else if (mesg == WM_SYSCOMMAND)
253 {
254 TRACE(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
255 co_IntSendMessage(Window->head.h, mesg, cmd, 0x00010000L);
256 }
257 else
258 {
259 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
260 * #0: unknown (please report!)
261 * #1: for WM_KEYUP,WM_SYSKEYUP
262 * #2: mouse is captured
263 * #3: window is disabled
264 * #4: it's a disabled system menu option
265 * #5: it's a menu option, but window is iconic
266 * #6: it's a menu option, but disabled
267 */
268 ERR(", but won't send WM_{SYS}COMMAND, reason is #%d\n", mesg);
269 if (mesg == 0)
270 {
271 ERR(" unknown reason - please report!");
272 }
273 }
274
275 TRACE("IntTranslateAccelerator(hWnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x) = TRUE\n",
276 Window->head.h, message, wParam, lParam, fVirt, key, cmd);
277
278 return TRUE;
279 }
280
281
282 /* SYSCALLS *****************************************************************/
283
284
285 int
286 APIENTRY
287 NtUserCopyAcceleratorTable(
288 HACCEL hAccel,
289 LPACCEL Entries,
290 int EntriesCount)
291 {
292 PACCELERATOR_TABLE Accel;
293 int Ret;
294 BOOL Done = FALSE;
295 DECLARE_RETURN(int);
296
297 TRACE("Enter NtUserCopyAcceleratorTable\n");
298 UserEnterShared();
299
300 Accel = UserGetAccelObject(hAccel);
301
302 if ((Entries && (EntriesCount < 1)) || ((hAccel == NULL) || (!Accel)))
303 {
304 RETURN(0);
305 }
306
307 if (Accel->Count < EntriesCount)
308 EntriesCount = Accel->Count;
309
310 Ret = 0;
311
312 _SEH2_TRY
313 {
314 ProbeForWrite(Entries, EntriesCount*sizeof(Entries[0]), 4);
315
316 while (!Done)
317 {
318 if (Entries)
319 {
320 Entries[Ret].fVirt = Accel->Table[Ret].fVirt & 0x7f;
321 Entries[Ret].key = Accel->Table[Ret].key;
322 Entries[Ret].cmd = Accel->Table[Ret].cmd;
323
324 if(Ret + 1 == EntriesCount) Done = TRUE;
325 }
326
327 if((Accel->Table[Ret].fVirt & 0x80) != 0) Done = TRUE;
328
329 Ret++;
330 }
331 }
332 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
333 {
334 SetLastNtError(_SEH2_GetExceptionCode());
335 Ret = 0;
336 }
337 _SEH2_END;
338
339 RETURN(Ret);
340
341 CLEANUP:
342 TRACE("Leave NtUserCopyAcceleratorTable, ret=%i\n",_ret_);
343 UserLeave();
344 END_CLEANUP;
345 }
346
347 HACCEL
348 APIENTRY
349 NtUserCreateAcceleratorTable(
350 LPACCEL Entries,
351 SIZE_T EntriesCount)
352 {
353 PACCELERATOR_TABLE Accel;
354 HACCEL hAccel;
355 INT Index;
356 NTSTATUS Status = STATUS_SUCCESS;
357 DECLARE_RETURN(HACCEL);
358
359 TRACE("Enter NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d)\n",
360 Entries, EntriesCount);
361 UserEnterExclusive();
362
363 if (!Entries || EntriesCount < 1)
364 {
365 SetLastNtError(STATUS_INVALID_PARAMETER);
366 RETURN( (HACCEL) NULL );
367 }
368
369 Accel = UserCreateObject(gHandleTable, NULL, (PHANDLE)&hAccel, otAccel, sizeof(ACCELERATOR_TABLE));
370
371 if (Accel == NULL)
372 {
373 SetLastNtError(STATUS_NO_MEMORY);
374 RETURN( (HACCEL) NULL );
375 }
376
377 Accel->Count = EntriesCount;
378 if (Accel->Count > 0)
379 {
380 Accel->Table = ExAllocatePoolWithTag(PagedPool, EntriesCount * sizeof(ACCEL), USERTAG_ACCEL);
381 if (Accel->Table == NULL)
382 {
383 UserDereferenceObject(Accel);
384 UserDeleteObject(hAccel, otAccel);
385 SetLastNtError(STATUS_NO_MEMORY);
386 RETURN( (HACCEL) NULL);
387 }
388
389 _SEH2_TRY
390 {
391 ProbeForRead(Entries, EntriesCount * sizeof(ACCEL), 4);
392
393 for (Index = 0; Index < EntriesCount; Index++)
394 {
395 Accel->Table[Index].fVirt = Entries[Index].fVirt&0x7f;
396 if(Accel->Table[Index].fVirt & FVIRTKEY)
397 {
398 Accel->Table[Index].key = Entries[Index].key;
399 }
400 else
401 {
402 RtlMultiByteToUnicodeN(&Accel->Table[Index].key,
403 sizeof(WCHAR),
404 NULL,
405 (PCSTR)&Entries[Index].key,
406 sizeof(CHAR));
407 }
408
409 Accel->Table[Index].cmd = Entries[Index].cmd;
410 }
411 }
412 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
413 {
414 Status = _SEH2_GetExceptionCode();
415 }
416 _SEH2_END;
417
418 if (!NT_SUCCESS(Status))
419 {
420 ExFreePoolWithTag(Accel->Table, USERTAG_ACCEL);
421 UserDereferenceObject(Accel);
422 UserDeleteObject(hAccel, otAccel);
423 SetLastNtError(Status);
424 RETURN( (HACCEL) NULL);
425 }
426
427 /* Set the end-of-table terminator. */
428 Accel->Table[EntriesCount - 1].fVirt |= 0x80;
429 }
430
431 /* FIXME: Save HandleTable in a list somewhere so we can clean it up again */
432
433 RETURN(hAccel);
434
435 CLEANUP:
436 TRACE("Leave NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d) = %x\n",
437 Entries, EntriesCount,_ret_);
438 UserLeave();
439 END_CLEANUP;
440 }
441
442 BOOLEAN
443 APIENTRY
444 NtUserDestroyAcceleratorTable(
445 HACCEL hAccel)
446 {
447 PACCELERATOR_TABLE Accel;
448 DECLARE_RETURN(BOOLEAN);
449
450 /* FIXME: If the handle table is from a call to LoadAcceleratorTable, decrement it's
451 usage count (and return TRUE).
452 FIXME: Destroy only tables created using CreateAcceleratorTable.
453 */
454
455 TRACE("NtUserDestroyAcceleratorTable(Table %x)\n", hAccel);
456 UserEnterExclusive();
457
458 if (!(Accel = UserGetAccelObject(hAccel)))
459 {
460 RETURN( FALSE);
461 }
462
463 if (Accel->Table != NULL)
464 {
465 ExFreePoolWithTag(Accel->Table, USERTAG_ACCEL);
466 Accel->Table = NULL;
467 }
468
469 UserDeleteObject(hAccel, otAccel);
470
471 RETURN( TRUE);
472
473 CLEANUP:
474 TRACE("Leave NtUserDestroyAcceleratorTable(Table %x) = %i\n", hAccel,_ret_);
475 UserLeave();
476 END_CLEANUP;
477 }
478
479 int
480 APIENTRY
481 NtUserTranslateAccelerator(
482 HWND hWnd,
483 HACCEL hAccel,
484 LPMSG pUnsafeMessage)
485 {
486 PWND Window = NULL;
487 PACCELERATOR_TABLE Accel = NULL;
488 ULONG i;
489 MSG Message;
490 USER_REFERENCE_ENTRY AccelRef, WindowRef;
491 DECLARE_RETURN(int);
492
493 TRACE("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p)\n",
494 hWnd, hAccel, pUnsafeMessage);
495 UserEnterShared();
496
497 if (pUnsafeMessage == NULL)
498 {
499 SetLastNtError(STATUS_INVALID_PARAMETER);
500 RETURN( 0);
501 }
502
503 _SEH2_TRY
504 {
505 ProbeForRead(pUnsafeMessage, sizeof(MSG), 4);
506 RtlCopyMemory(&Message, pUnsafeMessage, sizeof(MSG));
507 }
508 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
509 {
510 SetLastNtError(_SEH2_GetExceptionCode());
511 _SEH2_YIELD(RETURN( 0));
512 }
513 _SEH2_END;
514
515 if ((Message.message != WM_KEYDOWN) &&
516 (Message.message != WM_SYSKEYDOWN) &&
517 (Message.message != WM_SYSCHAR) &&
518 (Message.message != WM_CHAR))
519 {
520 RETURN( 0);
521 }
522
523 if (!(Accel = UserGetAccelObject(hAccel)))
524 {
525 RETURN( 0);
526 }
527
528 UserRefObjectCo(Accel, &AccelRef);
529
530 if (!(Window = UserGetWindowObject(hWnd)))
531 {
532 RETURN( 0);
533 }
534
535 UserRefObjectCo(Window, &WindowRef);
536
537 /* FIXME: Associate AcceleratorTable with the current thread */
538
539 for (i = 0; i < Accel->Count; i++)
540 {
541 if (co_IntTranslateAccelerator(Window, Message.message, Message.wParam, Message.lParam,
542 Accel->Table[i].fVirt, Accel->Table[i].key,
543 Accel->Table[i].cmd))
544 {
545 TRACE("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p) = %i end\n",
546 hWnd, hAccel, pUnsafeMessage, 1);
547 RETURN( 1);
548 }
549 if (((Accel->Table[i].fVirt & 0x80) > 0))
550 {
551 break;
552 }
553 }
554
555 RETURN( 0);
556
557 CLEANUP:
558 if (Window) UserDerefObjectCo(Window);
559 if (Accel) UserDerefObjectCo(Accel);
560
561 TRACE("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p) = %i end\n",
562 hWnd, hAccel, pUnsafeMessage, 0);
563 UserLeave();
564 END_CLEANUP;
565 }