[WIN32K]
[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,
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 BOOLEAN
133 IntRemoveEvent(PVOID Object)
134 {
135 PEVENTHOOK pEH = Object;
136 if (pEH)
137 {
138 TRACE("IntRemoveEvent pEH %p\n", pEH);
139 KeEnterCriticalRegion();
140 RemoveEntryList(&pEH->Chain);
141 GlobalEvents->Counts--;
142 if (!GlobalEvents->Counts) gpsi->dwInstalledEventHooks = 0;
143 UserDeleteObject(UserHMGetHandle(pEH), TYPE_WINEVENTHOOK);
144 KeLeaveCriticalRegion();
145 return TRUE;
146 }
147 return FALSE;
148 }
149
150 /* FUNCTIONS *****************************************************************/
151
152 //
153 // Dispatch MsgQueue Event Call processor!
154 //
155 LRESULT
156 FASTCALL
157 co_EVENT_CallEvents( DWORD event,
158 HWND hwnd,
159 UINT_PTR idObject,
160 LONG_PTR idChild)
161 {
162 PEVENTHOOK pEH;
163 LRESULT Result;
164 PEVENTPACK pEP = (PEVENTPACK)idChild;
165
166 pEH = pEP->pEH;
167
168 Result = co_IntCallEventProc( UserHMGetHandle(pEH),
169 event,
170 hwnd,
171 pEP->idObject,
172 pEP->idChild,
173 PtrToUint(NtCurrentTeb()->ClientId.UniqueThread),
174 (DWORD)EngGetTickCount(),
175 pEH->Proc);
176
177 ExFreePoolWithTag(pEP, TAG_HOOK);
178 return Result;
179 }
180
181 VOID
182 FASTCALL
183 IntNotifyWinEvent(
184 DWORD Event,
185 PWND pWnd,
186 LONG idObject,
187 LONG idChild,
188 DWORD flags)
189 {
190 PEVENTHOOK pEH;
191 PLIST_ENTRY pLE;
192 PTHREADINFO pti, ptiCurrent;
193
194 TRACE("IntNotifyWinEvent GlobalEvents = %p pWnd %p\n", GlobalEvents, pWnd);
195
196 if (!GlobalEvents || !GlobalEvents->Counts) return;
197
198 if (pWnd && pWnd->state & WNDS_DESTROYED) return;
199
200 ptiCurrent = PsGetCurrentThreadWin32Thread();
201
202 if (pWnd && flags & WEF_SETBYWNDPTI)
203 pti = pWnd->head.pti;
204 else
205 pti = ptiCurrent;
206
207 pLE = GlobalEvents->Events.Flink;
208 pEH = CONTAINING_RECORD(pLE, EVENTHOOK, Chain);
209 do
210 {
211 if (!pEH) break;
212 UserReferenceObject(pEH);
213 // Must be inside the event window.
214 if ( (pEH->eventMin <= Event) && (pEH->eventMax >= Event))
215 {
216 // if all process || all thread || other thread same process
217 // if ^skip own thread && ((Pid && CPid == Pid && ^skip own process) || all process)
218 if ( (!pEH->idProcess || pEH->idProcess == PtrToUint(pti->pEThread->Cid.UniqueProcess)) &&
219 (!(pEH->Flags & WINEVENT_SKIPOWNPROCESS) || pEH->head.pti->ppi != pti->ppi) &&
220 (!pEH->idThread || pEH->idThread == PtrToUint(pti->pEThread->Cid.UniqueThread)) &&
221 (!(pEH->Flags & WINEVENT_SKIPOWNTHREAD) || pEH->head.pti != pti) &&
222 pEH->head.pti->rpdesk == ptiCurrent->rpdesk ) // Same as hooks.
223 {
224 // Send message to the thread if pEH is not current.
225 if (pEH->head.pti != ptiCurrent)
226 {
227 ERR("Global Event 0x%x, idObject %d\n", Event, idObject);
228 IntCallLowLevelEvent( pEH,
229 Event,
230 pWnd ? UserHMGetHandle(pWnd) : NULL,
231 idObject,
232 idChild);
233 }
234 else
235 {
236 ERR("Local Event 0x%x, idObject %d\n", Event, idObject);
237 co_IntCallEventProc( UserHMGetHandle(pEH),
238 Event,
239 pWnd ? UserHMGetHandle(pWnd) : NULL,
240 idObject,
241 idChild,
242 PtrToUint(NtCurrentTeb()->ClientId.UniqueThread),
243 (DWORD)EngGetTickCount(),
244 pEH->Proc);
245 }
246 }
247 }
248 UserDereferenceObject(pEH);
249 pLE = pEH->Chain.Flink;
250 pEH = CONTAINING_RECORD(pLE, EVENTHOOK, Chain);
251 } while (pLE != &GlobalEvents->Events);
252 }
253
254 VOID
255 APIENTRY
256 NtUserNotifyWinEvent(
257 DWORD Event,
258 HWND hWnd,
259 LONG idObject,
260 LONG idChild)
261 {
262 PWND Window = NULL;
263 USER_REFERENCE_ENTRY Ref;
264 UserEnterExclusive();
265
266 /* Validate input */
267 if (hWnd && (hWnd != INVALID_HANDLE_VALUE))
268 {
269 Window = UserGetWindowObject(hWnd);
270 if (!Window)
271 {
272 UserLeave();
273 return;
274 }
275 }
276
277 if (gpsi->dwInstalledEventHooks & GetMaskFromEvent(Event))
278 {
279 if (Window) UserRefObjectCo(Window, &Ref);
280 IntNotifyWinEvent( Event, Window, idObject, idChild, WEF_SETBYWNDPTI);
281 if (Window) UserDerefObjectCo(Window);
282 }
283 UserLeave();
284 }
285
286 HWINEVENTHOOK
287 APIENTRY
288 NtUserSetWinEventHook(
289 UINT eventMin,
290 UINT eventMax,
291 HMODULE hmodWinEventProc,
292 PUNICODE_STRING puString,
293 WINEVENTPROC lpfnWinEventProc,
294 DWORD idProcess,
295 DWORD idThread,
296 UINT dwflags)
297 {
298 PEVENTHOOK pEH;
299 HWINEVENTHOOK Ret = NULL;
300 NTSTATUS Status;
301 HANDLE Handle;
302 PTHREADINFO pti;
303
304 TRACE("NtUserSetWinEventHook hmod %p, pfn %p\n", hmodWinEventProc, lpfnWinEventProc);
305
306 UserEnterExclusive();
307
308 if ( !GlobalEvents )
309 {
310 GlobalEvents = ExAllocatePoolWithTag(PagedPool, sizeof(EVENTTABLE), TAG_HOOK);
311 if (GlobalEvents == NULL)
312 {
313 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
314 goto SetEventExit;
315 }
316 GlobalEvents->Counts = 0;
317 InitializeListHead(&GlobalEvents->Events);
318 }
319
320 if (eventMin > eventMax)
321 {
322 EngSetLastError(ERROR_INVALID_HOOK_FILTER);
323 goto SetEventExit;
324 }
325
326 if (!lpfnWinEventProc)
327 {
328 EngSetLastError(ERROR_INVALID_FILTER_PROC);
329 goto SetEventExit;
330 }
331
332 if ((dwflags & WINEVENT_INCONTEXT) && !hmodWinEventProc)
333 {
334 EngSetLastError(ERROR_HOOK_NEEDS_HMOD);
335 goto SetEventExit;
336 }
337
338 if (idThread)
339 {
340 PETHREAD Thread;
341 Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)idThread, &Thread);
342 if (!NT_SUCCESS(Status))
343 {
344 EngSetLastError(ERROR_INVALID_THREAD_ID);
345 goto SetEventExit;
346 }
347 pti = PsGetThreadWin32Thread(Thread);
348 ObDereferenceObject(Thread);
349 }
350 else
351 {
352 pti = PsGetCurrentThreadWin32Thread();
353 }
354 // Creator, pti is set here.
355 pEH = UserCreateObject(gHandleTable, NULL, pti, &Handle, TYPE_WINEVENTHOOK, sizeof(EVENTHOOK));
356 if (pEH)
357 {
358 InsertTailList(&GlobalEvents->Events, &pEH->Chain);
359 GlobalEvents->Counts++;
360
361 UserHMGetHandle(pEH) = Handle;
362 pEH->eventMin = eventMin;
363 pEH->eventMax = eventMax;
364 pEH->idProcess = idProcess; // These are cmp'ed
365 pEH->idThread = idThread; // "
366 pEH->Flags = dwflags;
367 /*
368 If WINEVENT_INCONTEXT, set offset from hmod and proc. Save ihmod from
369 the atom index table where the hmod data is saved to be recalled later
370 if fSync set by WINEVENT_INCONTEXT.
371 If WINEVENT_OUTOFCONTEXT just use proc..
372 Do this instead....
373 */
374 if (NULL != hmodWinEventProc)
375 {
376 pEH->offPfn = (ULONG_PTR)((char *)lpfnWinEventProc - (char *)hmodWinEventProc);
377 pEH->ihmod = (INT)hmodWinEventProc;
378 pEH->Proc = lpfnWinEventProc;
379 }
380 else
381 pEH->Proc = lpfnWinEventProc;
382
383 UserDereferenceObject(pEH);
384
385 Ret = Handle;
386 IntSetSrvEventMask( eventMin, eventMax);
387 }
388
389 SetEventExit:
390 UserLeave();
391 return Ret;
392 }
393
394 BOOL
395 APIENTRY
396 NtUserUnhookWinEvent(
397 HWINEVENTHOOK hWinEventHook)
398 {
399 PEVENTHOOK pEH;
400 BOOL Ret = FALSE;
401
402 UserEnterExclusive();
403
404 pEH = (PEVENTHOOK)UserGetObject(gHandleTable, hWinEventHook, TYPE_WINEVENTHOOK);
405 if (pEH)
406 {
407 Ret = IntRemoveEvent(pEH);
408 }
409
410 UserLeave();
411 return Ret;
412 }
413
414 /* EOF */