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 = 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 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 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 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 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 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