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