[USER32] Add support for navigating a group of radio buttons using a keyboard.
[reactos.git] / win32ss / user / ntuser / event.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS kernel
4 * PURPOSE: Window event handlers
5 * FILE: win32ss/user/ntuser/event.c
6 * PROGRAMER: James Tabor (james.tabor@rectos.org)
7 */
8
9 #include <win32k.h>
10 DBG_DEFAULT_CHANNEL(UserEvent);
11
12 typedef struct _EVENTPACK
13 {
14 PEVENTHOOK pEH;
15 LONG idObject;
16 LONG idChild;
17 LONG idThread;
18 } EVENTPACK, *PEVENTPACK;
19
20 static PEVENTTABLE GlobalEvents = NULL;
21
22 /* PRIVATE FUNCTIONS *********************************************************/
23
24 static
25 DWORD
26 FASTCALL
27 GetMaskFromEvent(DWORD Event)
28 {
29 DWORD Ret = 0;
30
31 if ( Event > EVENT_OBJECT_STATECHANGE )
32 {
33 if ( Event == EVENT_OBJECT_LOCATIONCHANGE ) return SRV_EVENT_LOCATIONCHANGE;
34 if ( Event == EVENT_OBJECT_NAMECHANGE ) return SRV_EVENT_NAMECHANGE;
35 if ( Event == EVENT_OBJECT_VALUECHANGE ) return SRV_EVENT_VALUECHANGE;
36 return SRV_EVENT_CREATE;
37 }
38
39 if ( Event == EVENT_OBJECT_STATECHANGE ) return SRV_EVENT_STATECHANGE;
40
41 Ret = SRV_EVENT_RUNNING;
42
43 if ( Event < EVENT_SYSTEM_MENUSTART ) return SRV_EVENT_CREATE;
44
45 if ( Event <= EVENT_SYSTEM_MENUPOPUPEND )
46 {
47 Ret = SRV_EVENT_MENU;
48 }
49 else
50 {
51 if ( Event <= EVENT_CONSOLE_CARET-1 ) return SRV_EVENT_CREATE;
52 if ( Event <= EVENT_CONSOLE_END_APPLICATION ) return SRV_EVENT_END_APPLICATION;
53 if ( Event != EVENT_OBJECT_FOCUS ) return SRV_EVENT_CREATE;
54 }
55 return Ret;
56 }
57
58 static
59 VOID
60 FASTCALL
61 IntSetSrvEventMask( UINT EventMin, UINT EventMax)
62 {
63 UINT event;
64 TRACE("SetSrvEventMask 1\n");
65 for ( event = EventMin; event <= EventMax; event++)
66 {
67 if ((event >= EVENT_SYSTEM_SOUND && event <= EVENT_SYSTEM_MINIMIZEEND) ||
68 (event >= EVENT_CONSOLE_CARET && event <= EVENT_CONSOLE_END_APPLICATION) ||
69 (event >= EVENT_OBJECT_CREATE && event <= EVENT_OBJECT_ACCELERATORCHANGE))
70 {
71 gpsi->dwInstalledEventHooks |= GetMaskFromEvent(event);
72 }
73 if (event > EVENT_SYSTEM_MINIMIZEEND && event < EVENT_CONSOLE_CARET)
74 {
75 event = EVENT_CONSOLE_CARET-1;
76 gpsi->dwInstalledEventHooks |= GetMaskFromEvent(event);
77 }
78 if (event > EVENT_CONSOLE_END_APPLICATION && event < EVENT_OBJECT_CREATE )
79 {
80 event = EVENT_OBJECT_CREATE-1;
81 gpsi->dwInstalledEventHooks |= GetMaskFromEvent(event);
82 }
83 if (event > EVENT_OBJECT_ACCELERATORCHANGE && event < EVENT_MAX)
84 {
85 event = EVENT_MAX-1;
86 gpsi->dwInstalledEventHooks |= GetMaskFromEvent(event);
87 break;
88 }
89 }
90 if (!gpsi->dwInstalledEventHooks)
91 gpsi->dwInstalledEventHooks |= SRV_EVENT_RUNNING; // Set something.
92 TRACE("SetSrvEventMask 2 : %x\n", gpsi->dwInstalledEventHooks);
93 }
94
95 static
96 LRESULT
97 FASTCALL
98 IntCallLowLevelEvent( PEVENTHOOK pEH,
99 DWORD event,
100 HWND hwnd,
101 LONG idObject,
102 LONG idChild,
103 LONG idThread)
104 {
105 PEVENTPACK pEP;
106 MSG Msg;
107
108 pEP = ExAllocatePoolWithTag(NonPagedPool, sizeof(EVENTPACK), TAG_HOOK);
109 if (!pEP) return 0;
110
111 pEP->pEH = pEH;
112 pEP->idObject = idObject;
113 pEP->idChild = idChild;
114 pEP->idThread = idThread;
115
116 Msg.message = event;
117 Msg.hwnd = hwnd;
118 Msg.wParam = 0;
119 Msg.lParam = POSTEVENT_NWE;
120 Msg.time = 0;
121
122 MsqPostMessage(pEH->head.pti, &Msg, FALSE, QS_EVENT, POSTEVENT_NWE, (LONG_PTR)pEP);
123 return 0;
124 }
125
126 BOOLEAN
127 IntRemoveEvent(PVOID Object)
128 {
129 PEVENTHOOK pEH = Object;
130 if (pEH)
131 {
132 TRACE("IntRemoveEvent pEH %p\n", pEH);
133 KeEnterCriticalRegion();
134 RemoveEntryList(&pEH->Chain);
135 GlobalEvents->Counts--;
136 if (!GlobalEvents->Counts) gpsi->dwInstalledEventHooks = 0;
137 UserDeleteObject(UserHMGetHandle(pEH), TYPE_WINEVENTHOOK);
138 KeLeaveCriticalRegion();
139 return TRUE;
140 }
141 return FALSE;
142 }
143
144 /* FUNCTIONS *****************************************************************/
145
146 //
147 // Dispatch MsgQueue Event Call processor!
148 //
149 LRESULT
150 APIENTRY
151 co_EVENT_CallEvents( DWORD event,
152 HWND hwnd,
153 UINT_PTR idObject,
154 LONG_PTR idChild)
155 {
156 PEVENTHOOK pEH;
157 LRESULT Result;
158 PEVENTPACK pEP = (PEVENTPACK)idChild;
159
160 pEH = pEP->pEH;
161 TRACE("Dispatch Event 0x%lx, idObject %uI hwnd %p\n", event, idObject, hwnd);
162 Result = co_IntCallEventProc( UserHMGetHandle(pEH),
163 event,
164 hwnd,
165 pEP->idObject,
166 pEP->idChild,
167 pEP->idThread,
168 (DWORD)EngGetTickCount(),
169 pEH->Proc,
170 pEH->ihmod,
171 pEH->offPfn);
172
173 ExFreePoolWithTag(pEP, TAG_HOOK);
174 return Result;
175 }
176
177 VOID
178 FASTCALL
179 IntNotifyWinEvent(
180 DWORD Event,
181 PWND pWnd,
182 LONG idObject,
183 LONG idChild,
184 DWORD flags)
185 {
186 PEVENTHOOK pEH;
187 PLIST_ENTRY ListEntry;
188 PTHREADINFO pti, ptiCurrent;
189 USER_REFERENCE_ENTRY Ref;
190
191 TRACE("IntNotifyWinEvent GlobalEvents = %p pWnd %p\n", GlobalEvents, pWnd);
192
193 if (!GlobalEvents || !GlobalEvents->Counts) return;
194
195 if (pWnd && pWnd->state & WNDS_DESTROYED) return;
196
197 ptiCurrent = PsGetCurrentThreadWin32Thread();
198
199 if (pWnd && flags & WEF_SETBYWNDPTI)
200 pti = pWnd->head.pti;
201 else
202 pti = ptiCurrent;
203
204 ListEntry = GlobalEvents->Events.Flink;
205 ASSERT(ListEntry != &GlobalEvents->Events);
206 while (ListEntry != &GlobalEvents->Events)
207 {
208 pEH = CONTAINING_RECORD(ListEntry, EVENTHOOK, Chain);
209 ListEntry = ListEntry->Flink;
210
211 // Must be inside the event window.
212 if ( Event >= pEH->eventMin && Event <= pEH->eventMax )
213 {
214 // if all process || all thread || other thread same process
215 // if ^skip own thread && ((Pid && CPid == Pid && ^skip own process) || all process)
216 if (!( (pEH->idProcess && pEH->idProcess != PtrToUint(pti->pEThread->Cid.UniqueProcess)) ||
217 (pEH->Flags & WINEVENT_SKIPOWNPROCESS && pEH->head.pti->ppi == pti->ppi) ||
218 (pEH->idThread && pEH->idThread != PtrToUint(pti->pEThread->Cid.UniqueThread)) ||
219 (pEH->Flags & WINEVENT_SKIPOWNTHREAD && pEH->head.pti == pti) ||
220 pEH->head.pti->rpdesk != ptiCurrent->rpdesk ) ) // Same as hooks.
221 {
222 UserRefObjectCo(pEH, &Ref);
223 if (pEH->Flags & WINEVENT_INCONTEXT)
224 {
225 TRACE("In Event 0x%x, idObject %d hwnd %p\n", Event, idObject, pWnd ? UserHMGetHandle(pWnd) : NULL);
226 co_IntCallEventProc( UserHMGetHandle(pEH),
227 Event,
228 pWnd ? UserHMGetHandle(pWnd) : NULL,
229 idObject,
230 idChild,
231 PtrToUint(NtCurrentTeb()->ClientId.UniqueThread),
232 (DWORD)EngGetTickCount(),
233 pEH->Proc,
234 pEH->ihmod,
235 pEH->offPfn);
236 }
237 else
238 {
239 TRACE("Out Event 0x%x, idObject %d hwnd %p\n", Event, idObject, pWnd ? UserHMGetHandle(pWnd) : NULL);
240 IntCallLowLevelEvent( pEH,
241 Event,
242 pWnd ? UserHMGetHandle(pWnd) : NULL,
243 idObject,
244 idChild,
245 PtrToUint(NtCurrentTeb()->ClientId.UniqueThread));
246 }
247 UserDerefObjectCo(pEH);
248 }
249 }
250 }
251 }
252
253 VOID
254 APIENTRY
255 NtUserNotifyWinEvent(
256 DWORD Event,
257 HWND hWnd,
258 LONG idObject,
259 LONG idChild)
260 {
261 PWND Window = NULL;
262 USER_REFERENCE_ENTRY Ref;
263 UserEnterExclusive();
264
265 /* Validate input */
266 if (hWnd && (hWnd != INVALID_HANDLE_VALUE))
267 {
268 Window = UserGetWindowObject(hWnd);
269 if (!Window)
270 {
271 UserLeave();
272 return;
273 }
274 }
275
276 if (gpsi->dwInstalledEventHooks & GetMaskFromEvent(Event))
277 {
278 if (Window) UserRefObjectCo(Window, &Ref);
279 IntNotifyWinEvent( Event, Window, idObject, idChild, WEF_SETBYWNDPTI);
280 if (Window) UserDerefObjectCo(Window);
281 }
282 UserLeave();
283 }
284
285 HWINEVENTHOOK
286 APIENTRY
287 NtUserSetWinEventHook(
288 UINT eventMin,
289 UINT eventMax,
290 HMODULE hmodWinEventProc,
291 PUNICODE_STRING puString,
292 WINEVENTPROC lpfnWinEventProc,
293 DWORD idProcess,
294 DWORD idThread,
295 UINT dwflags)
296 {
297 PEVENTHOOK pEH;
298 HWINEVENTHOOK Ret = NULL;
299 NTSTATUS Status;
300 HANDLE Handle;
301 PTHREADINFO pti;
302
303 TRACE("NtUserSetWinEventHook hmod %p, pfn %p\n", hmodWinEventProc, lpfnWinEventProc);
304
305 UserEnterExclusive();
306
307 if ( !GlobalEvents )
308 {
309 GlobalEvents = ExAllocatePoolWithTag(PagedPool, sizeof(EVENTTABLE), TAG_HOOK);
310 if (GlobalEvents == NULL)
311 {
312 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
313 goto SetEventExit;
314 }
315 GlobalEvents->Counts = 0;
316 InitializeListHead(&GlobalEvents->Events);
317 }
318
319 if (eventMin > eventMax)
320 {
321 EngSetLastError(ERROR_INVALID_HOOK_FILTER);
322 goto SetEventExit;
323 }
324
325 if (!lpfnWinEventProc)
326 {
327 EngSetLastError(ERROR_INVALID_FILTER_PROC);
328 goto SetEventExit;
329 }
330
331 if (dwflags & WINEVENT_INCONTEXT)
332 {
333 if (!hmodWinEventProc)
334 {
335 ERR("Hook needs a module\n");
336 EngSetLastError(ERROR_HOOK_NEEDS_HMOD);
337 goto SetEventExit;
338 }
339 if (puString == NULL)
340 {
341 ERR("Dll not found\n");
342 EngSetLastError(ERROR_DLL_NOT_FOUND);
343 goto SetEventExit;
344 }
345 }
346 else
347 {
348 TRACE("Out of Context\n");
349 hmodWinEventProc = 0;
350 }
351
352 if (idThread)
353 {
354 PETHREAD Thread;
355 Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)idThread, &Thread);
356 if (!NT_SUCCESS(Status))
357 {
358 EngSetLastError(ERROR_INVALID_THREAD_ID);
359 goto SetEventExit;
360 }
361 pti = PsGetThreadWin32Thread(Thread);
362 ObDereferenceObject(Thread);
363 }
364 else
365 {
366 pti = PsGetCurrentThreadWin32Thread();
367 }
368 // Creator, pti is set here.
369 pEH = UserCreateObject(gHandleTable, NULL, pti, &Handle, TYPE_WINEVENTHOOK, sizeof(EVENTHOOK));
370 if (pEH)
371 {
372 InsertTailList(&GlobalEvents->Events, &pEH->Chain);
373 GlobalEvents->Counts++;
374
375 UserHMGetHandle(pEH) = Handle;
376 pEH->eventMin = eventMin;
377 pEH->eventMax = eventMax;
378 pEH->idProcess = idProcess; // These are cmp'ed
379 pEH->idThread = idThread; // "
380 pEH->Flags = dwflags;
381 /*
382 If WINEVENT_INCONTEXT, set offset from hmod and proc. Save ihmod from
383 the atom index table where the hmod data is saved to be recalled later
384 if fSync set by WINEVENT_INCONTEXT.
385 If WINEVENT_OUTOFCONTEXT just use proc..
386 Do this instead....
387 */
388 if (hmodWinEventProc != NULL)
389 {
390 pEH->offPfn = (ULONG_PTR)((char *)lpfnWinEventProc - (char *)hmodWinEventProc);
391 pEH->ihmod = (INT_PTR)hmodWinEventProc;
392 pEH->Proc = lpfnWinEventProc;
393 }
394 else
395 {
396 pEH->Proc = lpfnWinEventProc;
397 pEH->offPfn = 0;
398 pEH->ihmod = (INT_PTR)hmodWinEventProc;
399 }
400
401 UserDereferenceObject(pEH);
402
403 Ret = Handle;
404 IntSetSrvEventMask( eventMin, eventMax);
405 }
406
407 SetEventExit:
408 UserLeave();
409 return Ret;
410 }
411
412 BOOL
413 APIENTRY
414 NtUserUnhookWinEvent(
415 HWINEVENTHOOK hWinEventHook)
416 {
417 PEVENTHOOK pEH;
418 BOOL Ret = FALSE;
419
420 UserEnterExclusive();
421
422 pEH = (PEVENTHOOK)UserGetObject(gHandleTable, hWinEventHook, TYPE_WINEVENTHOOK);
423 if (pEH)
424 {
425 Ret = IntRemoveEvent(pEH);
426 }
427
428 UserLeave();
429 return Ret;
430 }
431
432 /* EOF */