[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 INIT_FUNCTION
44 NTSTATUS
45 NTAPI
46 InitAcceleratorImpl(VOID)
47 {
48 return(STATUS_SUCCESS);
49 }
50
51 NTSTATUS FASTCALL
52 CleanupAcceleratorImpl(VOID)
53 {
54 return(STATUS_SUCCESS);
55 }
56
57
58 PACCELERATOR_TABLE FASTCALL UserGetAccelObject(HACCEL hAccel)
59 {
60 PACCELERATOR_TABLE Accel;
61
62 if (!hAccel)
63 {
64 EngSetLastError(ERROR_INVALID_ACCEL_HANDLE);
65 return NULL;
66 }
67
68 Accel= UserGetObject(gHandleTable, hAccel, otAccel);
69 if (!Accel)
70 {
71 EngSetLastError(ERROR_INVALID_ACCEL_HANDLE);
72 return NULL;
73 }
74
75 ASSERT(Accel->head.cLockObj >= 0);
76
77 return Accel;
78 }
79
80
81 static
82 BOOLEAN FASTCALL
83 co_IntTranslateAccelerator(
84 PWND Window,
85 UINT message,
86 WPARAM wParam,
87 LPARAM lParam,
88 BYTE fVirt,
89 WORD key,
90 WORD cmd)
91 {
92 INT mask = 0;
93 UINT mesg = 0;
94 HWND hWnd;
95
96 ASSERT_REFS_CO(Window);
97
98 hWnd = Window->head.h;
99
100 TRACE("IntTranslateAccelerator(hwnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x)\n",
101 Window->head.h, message, wParam, lParam, fVirt, key, cmd);
102
103 if (wParam != key)
104 {
105 return FALSE;
106 }
107
108 TRACE("NtUserGetKeyState(VK_CONTROL) = 0x%x\n",UserGetKeyState(VK_CONTROL));
109 TRACE("NtUserGetKeyState(VK_MENU) = 0x%x\n",UserGetKeyState(VK_MENU));
110 TRACE("NtUserGetKeyState(VK_SHIFT) = 0x%x\n",UserGetKeyState(VK_SHIFT));
111
112 if (UserGetKeyState(VK_CONTROL) & 0x8000) mask |= FCONTROL;
113 if (UserGetKeyState(VK_MENU) & 0x8000) mask |= FALT;
114 if (UserGetKeyState(VK_SHIFT) & 0x8000) mask |= FSHIFT;
115 TRACE("Mask 0x%x\n",mask);
116
117 if (message == WM_CHAR || message == WM_SYSCHAR)
118 {
119 if ( !(fVirt & FVIRTKEY) && (mask & FALT) == (fVirt & FALT) )
120 {
121 TRACE("found accel for WM_CHAR: ('%c')\n", LOWORD(wParam) & 0xff);
122 goto found;
123 }
124 }
125 else
126 {
127 if (fVirt & FVIRTKEY)
128 {
129 TRACE("found accel for virt_key %04x (scan %04x)\n",
130 wParam, 0xff & HIWORD(lParam));
131
132 if (mask == (fVirt & (FSHIFT | FCONTROL | FALT))) goto found;
133 TRACE("but incorrect SHIFT/CTRL/ALT-state mask %x fVirt %x\n",mask,fVirt);
134 }
135 else
136 {
137 if (!(lParam & 0x01000000)) /* no special_key */
138 {
139 if ((fVirt & FALT) && (lParam & 0x20000000))
140 { /* ^^ ALT pressed */
141 TRACE("found accel for Alt-%c\n", LOWORD(wParam) & 0xff);
142 goto found;
143 }
144 }
145 }
146 }
147
148 TRACE("IntTranslateAccelerator(hwnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x) = FALSE\n",
149 Window->head.h, message, wParam, lParam, fVirt, key, cmd);
150
151 return FALSE;
152
153 found:
154 if (message == WM_KEYUP || message == WM_SYSKEYUP)
155 mesg = 1;
156 else
157 {
158 HMENU hMenu, hSubMenu, hSysMenu;
159 UINT uSysStat = (UINT)-1, uStat = (UINT)-1, nPos;
160 PMENU_OBJECT MenuObject, SubMenu;
161 PMENU_ITEM MenuItem;
162
163 hMenu = (Window->style & WS_CHILD) ? 0 : (HMENU)Window->IDMenu;
164 hSysMenu = Window->SystemMenu;
165 MenuObject = IntGetMenuObject(Window->SystemMenu);
166
167 /* find menu item and ask application to initialize it */
168 /* 1. in the system menu */
169 hSubMenu = hSysMenu;
170 if (MenuObject)
171 {
172 nPos = IntGetMenuItemByFlag( MenuObject,
173 cmd,
174 MF_BYCOMMAND,
175 &SubMenu,
176 &MenuItem,
177 NULL);
178
179 if (MenuItem && (nPos != (UINT)-1))
180 {
181 hSubMenu = MenuItem->hSubMenu;
182
183 if (IntGetCaptureWindow())
184 mesg = 2;
185 if (Window->style & WS_DISABLED)
186 mesg = 3;
187 else
188 {
189 co_IntSendMessage(hWnd, WM_INITMENU, (WPARAM)hSysMenu, 0L);
190 if (hSubMenu != hSysMenu)
191 {
192 nPos = IntFindSubMenu(&hSysMenu, hSubMenu);
193 TRACE("hSysMenu = %p, hSubMenu = %p, nPos = %d\n", hSysMenu, hSubMenu, nPos);
194 co_IntSendMessage(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, TRUE));
195 }
196 uSysStat = IntGetMenuState(IntGetSubMenu(hSysMenu, 0), cmd, MF_BYCOMMAND);
197 }
198 }
199 else /* 2. in the window's menu */
200 {
201 MenuObject = IntGetMenuObject(hMenu);
202 hSubMenu = hMenu;
203 if (MenuObject)
204 {
205 nPos = IntGetMenuItemByFlag( MenuObject,
206 cmd,
207 MF_BYCOMMAND,
208 &SubMenu,
209 &MenuItem,
210 NULL);
211
212 if (MenuItem && (nPos != (UINT)-1))
213 {
214 if (IntGetCaptureWindow())
215 mesg = 2;
216 if (Window->style & WS_DISABLED)
217 mesg = 3;
218 else
219 {
220 co_IntSendMessage(hWnd, WM_INITMENU, (WPARAM)hMenu, 0L);
221 if (hSubMenu != hMenu)
222 {
223 nPos = IntFindSubMenu(&hMenu, hSubMenu);
224 TRACE("hMenu = %p, hSubMenu = %p, nPos = %d\n", hMenu, hSubMenu, nPos);
225 co_IntSendMessage(hWnd, WM_INITMENUPOPUP, (WPARAM)hSubMenu, MAKELPARAM(nPos, FALSE));
226 }
227 uStat = IntGetMenuState(hMenu, cmd, MF_BYCOMMAND);
228 }
229 }
230 }
231 }
232 }
233 if (mesg == 0)
234 {
235 if (uSysStat != (UINT)-1)
236 {
237 if (uSysStat & (MF_DISABLED|MF_GRAYED))
238 mesg=4;
239 else
240 mesg=WM_SYSCOMMAND;
241 }
242 else
243 {
244 if (uStat != (UINT)-1)
245 {
246 if (Window->style & WS_MINIMIZE)
247 mesg=5;
248 else
249 {
250 if (uStat & (MF_DISABLED|MF_GRAYED))
251 mesg=6;
252 else
253 mesg=WM_COMMAND;
254 }
255 }
256 else
257 {
258 mesg=WM_COMMAND;
259 }
260 }
261 }
262 }
263
264 if (mesg == WM_COMMAND)
265 {
266 TRACE(", sending WM_COMMAND, wParam=%0x\n", 0x10000 | cmd);
267 co_IntSendMessage(Window->head.h, mesg, 0x10000 | cmd, 0L);
268 }
269 else if (mesg == WM_SYSCOMMAND)
270 {
271 TRACE(", sending WM_SYSCOMMAND, wParam=%0x\n", cmd);
272 co_IntSendMessage(Window->head.h, mesg, cmd, 0x00010000L);
273 }
274 else
275 {
276 /* some reasons for NOT sending the WM_{SYS}COMMAND message:
277 * #0: unknown (please report!)
278 * #1: for WM_KEYUP,WM_SYSKEYUP
279 * #2: mouse is captured
280 * #3: window is disabled
281 * #4: it's a disabled system menu option
282 * #5: it's a menu option, but window is iconic
283 * #6: it's a menu option, but disabled
284 */
285 ERR(", but won't send WM_{SYS}COMMAND, reason is #%d\n", mesg);
286 if (mesg == 0)
287 {
288 ERR(" unknown reason - please report!");
289 }
290 }
291
292 TRACE("IntTranslateAccelerator(hWnd %x, message %x, wParam %x, lParam %x, fVirt %d, key %x, cmd %x) = TRUE\n",
293 Window->head.h, message, wParam, lParam, fVirt, key, cmd);
294
295 return TRUE;
296 }
297
298
299 /* SYSCALLS *****************************************************************/
300
301
302 int
303 APIENTRY
304 NtUserCopyAcceleratorTable(
305 HACCEL hAccel,
306 LPACCEL Entries,
307 int EntriesCount)
308 {
309 PACCELERATOR_TABLE Accel;
310 int Ret;
311 BOOL Done = FALSE;
312 DECLARE_RETURN(int);
313
314 TRACE("Enter NtUserCopyAcceleratorTable\n");
315 UserEnterShared();
316
317 Accel = UserGetAccelObject(hAccel);
318
319 if ((Entries && (EntriesCount < 1)) || ((hAccel == NULL) || (!Accel)))
320 {
321 RETURN(0);
322 }
323
324 if (Accel->Count < EntriesCount)
325 EntriesCount = Accel->Count;
326
327 Ret = 0;
328
329 while (!Done)
330 {
331 if (Entries)
332 {
333 Entries[Ret].fVirt = Accel->Table[Ret].fVirt & 0x7f;
334 Entries[Ret].key = Accel->Table[Ret].key;
335 Entries[Ret].cmd = Accel->Table[Ret].cmd;
336
337 if(Ret + 1 == EntriesCount) Done = TRUE;
338 }
339
340 if((Accel->Table[Ret].fVirt & 0x80) != 0) Done = TRUE;
341
342 Ret++;
343 }
344
345 RETURN(Ret);
346
347 CLEANUP:
348 TRACE("Leave NtUserCopyAcceleratorTable, ret=%i\n",_ret_);
349 UserLeave();
350 END_CLEANUP;
351 }
352
353 HACCEL
354 APIENTRY
355 NtUserCreateAcceleratorTable(
356 LPACCEL Entries,
357 SIZE_T EntriesCount)
358 {
359 PACCELERATOR_TABLE Accel;
360 HACCEL hAccel;
361 INT Index;
362 DECLARE_RETURN(HACCEL);
363
364 TRACE("Enter NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d)\n",
365 Entries, EntriesCount);
366 UserEnterExclusive();
367
368 if (!Entries || EntriesCount < 1)
369 {
370 SetLastNtError(STATUS_INVALID_PARAMETER);
371 RETURN( (HACCEL) NULL );
372 }
373
374 Accel = UserCreateObject(gHandleTable, NULL, (PHANDLE)&hAccel, otAccel, sizeof(ACCELERATOR_TABLE));
375
376 if (Accel == NULL)
377 {
378 SetLastNtError(STATUS_NO_MEMORY);
379 RETURN( (HACCEL) NULL );
380 }
381
382 Accel->Count = EntriesCount;
383 if (Accel->Count > 0)
384 {
385 Accel->Table = ExAllocatePoolWithTag(PagedPool, EntriesCount * sizeof(ACCEL), USERTAG_ACCEL);
386 if (Accel->Table == NULL)
387 {
388 UserDereferenceObject(Accel);
389 UserDeleteObject(hAccel, otAccel);
390 SetLastNtError(STATUS_NO_MEMORY);
391 RETURN( (HACCEL) NULL);
392 }
393
394 for (Index = 0; Index < EntriesCount; Index++)
395 {
396 Accel->Table[Index].fVirt = Entries[Index].fVirt&0x7f;
397 if(Accel->Table[Index].fVirt & FVIRTKEY)
398 {
399 Accel->Table[Index].key = Entries[Index].key;
400 }
401 else
402 {
403 RtlMultiByteToUnicodeN(&Accel->Table[Index].key,
404 sizeof(WCHAR),
405 NULL,
406 (PCSTR)&Entries[Index].key,
407 sizeof(CHAR));
408 }
409
410 Accel->Table[Index].cmd = Entries[Index].cmd;
411 }
412
413 /* Set the end-of-table terminator. */
414 Accel->Table[EntriesCount - 1].fVirt |= 0x80;
415 }
416
417 /* FIXME: Save HandleTable in a list somewhere so we can clean it up again */
418
419 RETURN(hAccel);
420
421 CLEANUP:
422 TRACE("Leave NtUserCreateAcceleratorTable(Entries %p, EntriesCount %d) = %x\n",
423 Entries, EntriesCount,_ret_);
424 UserLeave();
425 END_CLEANUP;
426 }
427
428 BOOLEAN
429 APIENTRY
430 NtUserDestroyAcceleratorTable(
431 HACCEL hAccel)
432 {
433 PACCELERATOR_TABLE Accel;
434 DECLARE_RETURN(BOOLEAN);
435
436 /* FIXME: If the handle table is from a call to LoadAcceleratorTable, decrement it's
437 usage count (and return TRUE).
438 FIXME: Destroy only tables created using CreateAcceleratorTable.
439 */
440
441 TRACE("NtUserDestroyAcceleratorTable(Table %x)\n", hAccel);
442 UserEnterExclusive();
443
444 if (!(Accel = UserGetAccelObject(hAccel)))
445 {
446 RETURN( FALSE);
447 }
448
449 if (Accel->Table != NULL)
450 {
451 ExFreePoolWithTag(Accel->Table, USERTAG_ACCEL);
452 Accel->Table = NULL;
453 }
454
455 UserDeleteObject(hAccel, otAccel);
456
457 RETURN( TRUE);
458
459 CLEANUP:
460 TRACE("Leave NtUserDestroyAcceleratorTable(Table %x) = %i\n", hAccel,_ret_);
461 UserLeave();
462 END_CLEANUP;
463 }
464
465 int
466 APIENTRY
467 NtUserTranslateAccelerator(
468 HWND hWnd,
469 HACCEL hAccel,
470 LPMSG Message)
471 {
472 PWND Window = NULL;
473 PACCELERATOR_TABLE Accel = NULL;
474 ULONG i;
475 USER_REFERENCE_ENTRY AccelRef, WindowRef;
476 DECLARE_RETURN(int);
477
478 TRACE("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p)\n",
479 hWnd, hAccel, Message);
480 UserEnterShared();
481
482 if (Message == NULL)
483 {
484 SetLastNtError(STATUS_INVALID_PARAMETER);
485 RETURN( 0);
486 }
487
488 if ((Message->message != WM_KEYDOWN) &&
489 (Message->message != WM_SYSKEYDOWN) &&
490 (Message->message != WM_SYSCHAR) &&
491 (Message->message != WM_CHAR))
492 {
493 RETURN( 0);
494 }
495
496 if (!(Accel = UserGetAccelObject(hAccel)))
497 {
498 RETURN( 0);
499 }
500
501 UserRefObjectCo(Accel, &AccelRef);
502
503 if (!(Window = UserGetWindowObject(hWnd)))
504 {
505 RETURN( 0);
506 }
507
508 UserRefObjectCo(Window, &WindowRef);
509
510
511 /* FIXME: Associate AcceleratorTable with the current thread */
512
513 for (i = 0; i < Accel->Count; i++)
514 {
515 if (co_IntTranslateAccelerator(Window, Message->message, Message->wParam, Message->lParam,
516 Accel->Table[i].fVirt, Accel->Table[i].key,
517 Accel->Table[i].cmd))
518 {
519 TRACE("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p) = %i end\n",
520 hWnd, hAccel, Message, 1);
521 RETURN( 1);
522 }
523 if (((Accel->Table[i].fVirt & 0x80) > 0))
524 {
525 break;
526 }
527 }
528
529 RETURN( 0);
530
531 CLEANUP:
532 if (Window) UserDerefObjectCo(Window);
533 if (Accel) UserDerefObjectCo(Accel);
534
535 TRACE("NtUserTranslateAccelerator(hWnd %x, Table %x, Message %p) = %i end\n",
536 hWnd, hAccel, Message, 0);
537 UserLeave();
538 END_CLEANUP;
539 }