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 } 456