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)
8 * 06-06-2001 CSH Created
12 * Copyright 1993 Martin Ayotte
13 * Copyright 1994 Alexandre Julliard
14 * Copyright 1997 Morten Welinder
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.
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.
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
31 /* INCLUDES ******************************************************************/
35 DBG_DEFAULT_CHANNEL(UserAccel
);
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
);
41 /* FUNCTIONS *****************************************************************/
43 PACCELERATOR_TABLE FASTCALL
UserGetAccelObject(HACCEL hAccel
)
45 PACCELERATOR_TABLE Accel
;
49 EngSetLastError(ERROR_INVALID_ACCEL_HANDLE
);
53 Accel
= UserGetObject(gHandleTable
, hAccel
, otAccel
);
56 EngSetLastError(ERROR_INVALID_ACCEL_HANDLE
);
66 co_IntTranslateAccelerator(
79 ASSERT_REFS_CO(Window
);
81 hWnd
= Window
->head
.h
;
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
);
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
));
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
);
100 if (message
== WM_CHAR
|| message
== WM_SYSCHAR
)
102 if ( !(fVirt
& FVIRTKEY
) && (mask
& FALT
) == (fVirt
& FALT
) )
104 TRACE("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam
) & 0xff);
110 if (fVirt
& FVIRTKEY
)
112 TRACE("found accel for virt_key %04x (scan %04x)\n",
113 wParam
, 0xff & HIWORD(lParam
));
115 if (mask
== (fVirt
& (FSHIFT
| FCONTROL
| FALT
))) goto found
;
116 TRACE("but incorrect SHIFT/CTRL/ALT-state mask %x fVirt %x\n",mask
,fVirt
);
120 if (!(lParam
& 0x01000000)) /* no special_key */
122 if ((fVirt
& FALT
) && (lParam
& 0x20000000))
123 { /* ^^ ALT pressed */
124 TRACE("found accel for Alt-%c\n", LOWORD(wParam
) & 0xff);
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
);
137 if (message
== WM_KEYUP
|| message
== WM_SYSKEYUP
)
141 HMENU hMenu
, hSubMenu
, hSysMenu
;
142 UINT uSysStat
= (UINT
)-1, uStat
= (UINT
)-1, nPos
;
143 PMENU_OBJECT MenuObject
, SubMenu
;
146 hMenu
= (Window
->style
& WS_CHILD
) ? 0 : (HMENU
)Window
->IDMenu
;
147 hSysMenu
= Window
->SystemMenu
;
148 MenuObject
= IntGetMenuObject(Window
->SystemMenu
);
150 /* find menu item and ask application to initialize it */
151 /* 1. in the system menu */
155 nPos
= IntGetMenuItemByFlag( MenuObject
,
162 if (MenuItem
&& (nPos
!= (UINT
)-1))
164 hSubMenu
= MenuItem
->hSubMenu
;
166 if (IntGetCaptureWindow())
168 if (Window
->style
& WS_DISABLED
)
172 co_IntSendMessage(hWnd
, WM_INITMENU
, (WPARAM
)hSysMenu
, 0L);
173 if (hSubMenu
!= hSysMenu
)
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
));
179 uSysStat
= IntGetMenuState(IntGetSubMenu(hSysMenu
, 0), cmd
, MF_BYCOMMAND
);
182 else /* 2. in the window's menu */
184 MenuObject
= IntGetMenuObject(hMenu
);
188 nPos
= IntGetMenuItemByFlag( MenuObject
,
195 if (MenuItem
&& (nPos
!= (UINT
)-1))
197 if (IntGetCaptureWindow())
199 if (Window
->style
& WS_DISABLED
)
203 co_IntSendMessage(hWnd
, WM_INITMENU
, (WPARAM
)hMenu
, 0L);
204 if (hSubMenu
!= hMenu
)
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
));
210 uStat
= IntGetMenuState(hMenu
, cmd
, MF_BYCOMMAND
);
218 if (uSysStat
!= (UINT
)-1)
220 if (uSysStat
& (MF_DISABLED
|MF_GRAYED
))
227 if (uStat
!= (UINT
)-1)
229 if (Window
->style
& WS_MINIMIZE
)
233 if (uStat
& (MF_DISABLED
|MF_GRAYED
))
247 if (mesg
== WM_COMMAND
)
249 TRACE(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd
);
250 co_IntSendMessage(Window
->head
.h
, mesg
, 0x10000 | cmd
, 0L);
252 else if (mesg
== WM_SYSCOMMAND
)
254 TRACE(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd
);
255 co_IntSendMessage(Window
->head
.h
, mesg
, cmd
, 0x00010000L
);
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
268 ERR(", but won't send WM_{SYS}COMMAND, reason is #%d\n", mesg
);
271 ERR(" unknown reason - please report!");
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
);
282 /* SYSCALLS *****************************************************************/
287 NtUserCopyAcceleratorTable(
292 PACCELERATOR_TABLE Accel
;
297 TRACE("Enter NtUserCopyAcceleratorTable\n");
300 Accel
= UserGetAccelObject(hAccel
);
302 if ((Entries
&& (EntriesCount
< 1)) || ((hAccel
== NULL
) || (!Accel
)))
307 if (Accel
->Count
< EntriesCount
)
308 EntriesCount
= Accel
->Count
;
314 ProbeForWrite(Entries
, EntriesCount
*sizeof(Entries
[0]), 4);
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
;
324 if(Ret
+ 1 == EntriesCount
) Done
= TRUE
;
327 if((Accel
->Table
[Ret
].fVirt
& 0x80) != 0) Done
= TRUE
;
332 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
334 SetLastNtError(_SEH2_GetExceptionCode());
342 TRACE("Leave NtUserCopyAcceleratorTable, ret=%i\n",_ret_
);
349 NtUserCreateAcceleratorTable(
353 PACCELERATOR_TABLE Accel
;
356 NTSTATUS Status
= STATUS_SUCCESS
;
357 DECLARE_RETURN(HACCEL
);
359 TRACE("Enter NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d)\n",
360 Entries
, EntriesCount
);
361 UserEnterExclusive();
363 if (!Entries
|| EntriesCount
< 1)
365 SetLastNtError(STATUS_INVALID_PARAMETER
);
366 RETURN( (HACCEL
) NULL
);
369 Accel
= UserCreateObject(gHandleTable
, NULL
, (PHANDLE
)&hAccel
, otAccel
, sizeof(ACCELERATOR_TABLE
));
373 SetLastNtError(STATUS_NO_MEMORY
);
374 RETURN( (HACCEL
) NULL
);
377 Accel
->Count
= EntriesCount
;
378 if (Accel
->Count
> 0)
380 Accel
->Table
= ExAllocatePoolWithTag(PagedPool
, EntriesCount
* sizeof(ACCEL
), USERTAG_ACCEL
);
381 if (Accel
->Table
== NULL
)
383 UserDereferenceObject(Accel
);
384 UserDeleteObject(hAccel
, otAccel
);
385 SetLastNtError(STATUS_NO_MEMORY
);
386 RETURN( (HACCEL
) NULL
);
391 ProbeForRead(Entries
, EntriesCount
* sizeof(ACCEL
), 4);
393 for (Index
= 0; Index
< EntriesCount
; Index
++)
395 Accel
->Table
[Index
].fVirt
= Entries
[Index
].fVirt
&0x7f;
396 if(Accel
->Table
[Index
].fVirt
& FVIRTKEY
)
398 Accel
->Table
[Index
].key
= Entries
[Index
].key
;
402 RtlMultiByteToUnicodeN(&Accel
->Table
[Index
].key
,
405 (PCSTR
)&Entries
[Index
].key
,
409 Accel
->Table
[Index
].cmd
= Entries
[Index
].cmd
;
412 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
414 Status
= _SEH2_GetExceptionCode();
418 if (!NT_SUCCESS(Status
))
420 ExFreePoolWithTag(Accel
->Table
, USERTAG_ACCEL
);
421 UserDereferenceObject(Accel
);
422 UserDeleteObject(hAccel
, otAccel
);
423 SetLastNtError(Status
);
424 RETURN( (HACCEL
) NULL
);
427 /* Set the end-of-table terminator. */
428 Accel
->Table
[EntriesCount
- 1].fVirt
|= 0x80;
431 /* FIXME: Save HandleTable in a list somewhere so we can clean it up again */
436 TRACE("Leave NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d) = %x\n",
437 Entries
, EntriesCount
,_ret_
);
444 NtUserDestroyAcceleratorTable(
447 PACCELERATOR_TABLE Accel
;
448 DECLARE_RETURN(BOOLEAN
);
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.
455 TRACE("NtUserDestroyAcceleratorTable(Table %x)\n", hAccel
);
456 UserEnterExclusive();
458 if (!(Accel
= UserGetAccelObject(hAccel
)))
463 if (Accel
->Table
!= NULL
)
465 ExFreePoolWithTag(Accel
->Table
, USERTAG_ACCEL
);
469 UserDeleteObject(hAccel
, otAccel
);
474 TRACE("Leave NtUserDestroyAcceleratorTable(Table %x) = %i\n", hAccel
,_ret_
);
481 NtUserTranslateAccelerator(
484 LPMSG pUnsafeMessage
)
487 PACCELERATOR_TABLE Accel
= NULL
;
490 USER_REFERENCE_ENTRY AccelRef
, WindowRef
;
493 TRACE("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p)\n",
494 hWnd
, hAccel
, pUnsafeMessage
);
497 if (pUnsafeMessage
== NULL
)
499 SetLastNtError(STATUS_INVALID_PARAMETER
);
505 ProbeForRead(pUnsafeMessage
, sizeof(MSG
), 4);
506 RtlCopyMemory(&Message
, pUnsafeMessage
, sizeof(MSG
));
508 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER
)
510 SetLastNtError(_SEH2_GetExceptionCode());
511 _SEH2_YIELD(RETURN( 0));
515 if ((Message
.message
!= WM_KEYDOWN
) &&
516 (Message
.message
!= WM_SYSKEYDOWN
) &&
517 (Message
.message
!= WM_SYSCHAR
) &&
518 (Message
.message
!= WM_CHAR
))
523 if (!(Accel
= UserGetAccelObject(hAccel
)))
528 UserRefObjectCo(Accel
, &AccelRef
);
530 if (!(Window
= UserGetWindowObject(hWnd
)))
535 UserRefObjectCo(Window
, &WindowRef
);
537 /* FIXME: Associate AcceleratorTable with the current thread */
539 for (i
= 0; i
< Accel
->Count
; i
++)
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
))
545 TRACE("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p) = %i end\n",
546 hWnd
, hAccel
, pUnsafeMessage
, 1);
549 if (((Accel
->Table
[i
].fVirt
& 0x80) > 0))
558 if (Window
) UserDerefObjectCo(Window
);
559 if (Accel
) UserDerefObjectCo(Accel
);
561 TRACE("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p) = %i end\n",
562 hWnd
, hAccel
, pUnsafeMessage
, 0);