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