sync with trunk (r49238)
[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 5000,
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) && !(Window = UserGetWindowObject(hWnd)))
303 {
304 UserLeave();
305 return;
306 }
307
308 if (gpsi->dwInstalledEventHooks & GetMaskFromEvent(Event))
309 {
310 UserRefObjectCo(Window, &Ref);
311 IntNotifyWinEvent( Event, Window, idObject, idChild, WEF_SETBYWNDPTI);
312 UserDerefObjectCo(Window);
313 }
314 UserLeave();
315 }
316
317 HWINEVENTHOOK
318 APIENTRY
319 NtUserSetWinEventHook(
320 UINT eventMin,
321 UINT eventMax,
322 HMODULE hmodWinEventProc,
323 PUNICODE_STRING puString,
324 WINEVENTPROC lpfnWinEventProc,
325 DWORD idProcess,
326 DWORD idThread,
327 UINT dwflags)
328 {
329 PEVENTHOOK pEH;
330 HWINEVENTHOOK Ret = NULL;
331 NTSTATUS Status;
332 HANDLE Handle;
333 PETHREAD Thread = NULL;
334
335 DPRINT("NtUserSetWinEventHook hmod 0x%x, pfn 0x%x\n",hmodWinEventProc, lpfnWinEventProc);
336
337 UserEnterExclusive();
338
339 if ( !GlobalEvents )
340 {
341 GlobalEvents = ExAllocatePoolWithTag(PagedPool, sizeof(EVENTTABLE), TAG_HOOK);
342 if (GlobalEvents == NULL)
343 {
344 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
345 goto SetEventExit;
346 }
347 GlobalEvents->Counts = 0;
348 InitializeListHead(&GlobalEvents->Events);
349 }
350
351 if (eventMin > eventMax)
352 {
353 SetLastWin32Error(ERROR_INVALID_HOOK_FILTER);
354 goto SetEventExit;
355 }
356
357 if (!lpfnWinEventProc)
358 {
359 SetLastWin32Error(ERROR_INVALID_FILTER_PROC);
360 goto SetEventExit;
361 }
362
363 if ((dwflags & WINEVENT_INCONTEXT) && !hmodWinEventProc)
364 {
365 SetLastWin32Error(ERROR_HOOK_NEEDS_HMOD);
366 goto SetEventExit;
367 }
368
369 if (idThread)
370 {
371 Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)idThread, &Thread);
372 if (!NT_SUCCESS(Status))
373 {
374 SetLastWin32Error(ERROR_INVALID_THREAD_ID);
375 goto SetEventExit;
376 }
377 }
378 // Creator, pti is set here.
379 pEH = UserCreateObject(gHandleTable, NULL, &Handle, otEvent, sizeof(EVENTHOOK));
380 if (pEH)
381 {
382 InsertTailList(&GlobalEvents->Events, &pEH->Chain);
383 GlobalEvents->Counts++;
384
385 UserHMGetHandle(pEH) = Handle;
386 pEH->eventMin = eventMin;
387 pEH->eventMax = eventMax;
388 pEH->idProcess = idProcess; // These are cmp'ed
389 pEH->idThread = idThread; // "
390 pEH->Flags = dwflags;
391 /*
392 If WINEVENT_INCONTEXT, set offset from hmod and proc. Save ihmod from
393 the atom index table where the hmod data is saved to be recalled later
394 if fSync set by WINEVENT_INCONTEXT.
395 If WINEVENT_OUTOFCONTEXT just use proc..
396 Do this instead....
397 */
398 if (NULL != hmodWinEventProc)
399 {
400 pEH->offPfn = (ULONG_PTR)((char *)lpfnWinEventProc - (char *)hmodWinEventProc);
401 pEH->ihmod = (INT)hmodWinEventProc;
402 pEH->Proc = lpfnWinEventProc;
403 }
404 else
405 pEH->Proc = lpfnWinEventProc;
406
407 UserDereferenceObject(pEH);
408
409 Ret = Handle;
410 IntSetSrvEventMask( eventMin, eventMax);
411 }
412
413 SetEventExit:
414 if (Thread) ObDereferenceObject(Thread);
415 UserLeave();
416 return Ret;
417 }
418
419 BOOL
420 APIENTRY
421 NtUserUnhookWinEvent(
422 HWINEVENTHOOK hWinEventHook)
423 {
424 PEVENTHOOK pEH;
425 BOOL Ret = FALSE;
426
427 UserEnterExclusive();
428
429 pEH = (PEVENTHOOK)UserGetObject(gHandleTable, hWinEventHook, otEvent);
430 if (pEH)
431 {
432 Ret = IntRemoveEvent(pEH);
433 }
434
435 UserLeave();
436 return Ret;
437 }
438
439 /* EOF */