sync with trunk r47227
[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: subsystem/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 ULONG_PTR uResult;
107 EVENTPACK EP;
108
109 EP.pEH = pEH;
110 EP.idObject = idObject;
111 EP.idChild = idChild;
112
113 /* FIXME should get timeout from
114 * HKEY_CURRENT_USER\Control Panel\Desktop\LowLevelHooksTimeout */
115 Status = co_MsqSendMessage( pEH->head.pti->MessageQueue,
116 hwnd,
117 event,
118 0,
119 (LPARAM)&EP,
120 5000,
121 TRUE,
122 MSQ_ISEVENT,
123 &uResult);
124
125 return NT_SUCCESS(Status) ? uResult : 0;
126 }
127
128
129 static
130 BOOL
131 FASTCALL
132 IntRemoveEvent(PEVENTHOOK pEH)
133 {
134 if (pEH)
135 {
136 DPRINT("IntRemoveEvent pEH 0x%x\n",pEH);
137 KeEnterCriticalRegion();
138 RemoveEntryList(&pEH->Chain);
139 GlobalEvents->Counts--;
140 if (!GlobalEvents->Counts) gpsi->dwInstalledEventHooks = 0;
141 UserDeleteObject(UserHMGetHandle(pEH), otEvent);
142 KeLeaveCriticalRegion();
143 return TRUE;
144 }
145 return FALSE;
146 }
147
148 /* FUNCTIONS *****************************************************************/
149
150 LRESULT
151 FASTCALL
152 co_EVENT_CallEvents( DWORD event,
153 HWND hwnd,
154 UINT_PTR idObject,
155 LONG_PTR idChild)
156 {
157 PEVENTHOOK pEH;
158 LRESULT Result;
159 PEVENTPACK pEP = (PEVENTPACK)idChild;
160
161 pEH = pEP->pEH;
162
163 Result = co_IntCallEventProc( UserHMGetHandle(pEH),
164 event,
165 hwnd,
166 pEP->idObject,
167 pEP->idChild,
168 (DWORD_PTR)(NtCurrentTeb()->ClientId).UniqueThread,
169 (DWORD)EngGetTickCount(),
170 pEH->Proc);
171 return Result;
172 }
173
174 VOID
175 FASTCALL
176 IntNotifyWinEvent(
177 DWORD Event,
178 PWND pWnd,
179 LONG idObject,
180 LONG idChild)
181 {
182 PEVENTHOOK pEH;
183 PLIST_ENTRY pLE;
184 LRESULT Result;
185
186 DPRINT("IntNotifyWinEvent GlobalEvents = 0x%x pWnd 0x%x\n",GlobalEvents, pWnd);
187
188 if (!pWnd) return;
189
190 if (pWnd && pWnd->state & WNDS_DESTROYED) return;
191
192 if (!GlobalEvents || !GlobalEvents->Counts) return;
193
194 pLE = GlobalEvents->Events.Flink;
195 pEH = CONTAINING_RECORD(pLE, EVENTHOOK, Chain);
196 do
197 {
198 UserReferenceObject(pEH);
199 // Must be inside the event window.
200 if ( (pEH->eventMin <= Event) && (pEH->eventMax >= Event))
201 {
202 if (pEH->head.pti->pEThread != PsGetCurrentThread())
203 { // if all process || all thread || other thread same process
204 if (!(pEH->idProcess) || !(pEH->idThread) ||
205 (NtCurrentTeb()->ClientId.UniqueProcess == (PVOID)(DWORD_PTR)pEH->idProcess))
206 {
207 Result = IntCallLowLevelEvent( pEH,
208 Event,
209 UserHMGetHandle(pWnd),
210 idObject,
211 idChild);
212 }
213 }// if ^skip own thread && ((Pid && CPid == Pid && ^skip own process) || all process)
214 else if ( !(pEH->Flags & WINEVENT_SKIPOWNTHREAD) &&
215 ( ((pEH->idProcess &&
216 NtCurrentTeb()->ClientId.UniqueProcess == (PVOID)(DWORD_PTR)pEH->idProcess) &&
217 !(pEH->Flags & WINEVENT_SKIPOWNPROCESS)) ||
218 !pEH->idProcess ) )
219 {
220 Result = co_IntCallEventProc( UserHMGetHandle(pEH),
221 Event,
222 UserHMGetHandle(pWnd),
223 idObject,
224 idChild,
225 PtrToUint(NtCurrentTeb()->ClientId.UniqueThread),
226 (DWORD)EngGetTickCount(),
227 pEH->Proc);
228 }
229 }
230 UserDereferenceObject(pEH);
231 pLE = pEH->Chain.Flink;
232 pEH = CONTAINING_RECORD(pLE, EVENTHOOK, Chain);
233 } while (pLE != &GlobalEvents->Events);
234 }
235
236 VOID
237 APIENTRY
238 NtUserNotifyWinEvent(
239 DWORD Event,
240 HWND hWnd,
241 LONG idObject,
242 LONG idChild)
243 {
244 PWINDOW_OBJECT Window = NULL;
245 USER_REFERENCE_ENTRY Ref;
246 UserEnterExclusive();
247
248 /* Validate input */
249 if (hWnd && (hWnd != INVALID_HANDLE_VALUE) && !(Window = UserGetWindowObject(hWnd)))
250 {
251 UserLeave();
252 return;
253 }
254
255 if (gpsi->dwInstalledEventHooks & GetMaskFromEvent(Event))
256 {
257 UserRefObjectCo(Window, &Ref);
258 IntNotifyWinEvent( Event, Window->Wnd, idObject, idChild);
259 UserDerefObjectCo(Window);
260 }
261 UserLeave();
262 }
263
264 HWINEVENTHOOK
265 APIENTRY
266 NtUserSetWinEventHook(
267 UINT eventMin,
268 UINT eventMax,
269 HMODULE hmodWinEventProc,
270 PUNICODE_STRING puString,
271 WINEVENTPROC lpfnWinEventProc,
272 DWORD idProcess,
273 DWORD idThread,
274 UINT dwflags)
275 {
276 PEVENTHOOK pEH;
277 HWINEVENTHOOK Ret = NULL;
278 NTSTATUS Status;
279 HANDLE Handle;
280 PETHREAD Thread = NULL;
281
282 DPRINT("NtUserSetWinEventHook hmod 0x%x, pfn 0x%x\n",hmodWinEventProc, lpfnWinEventProc);
283
284 UserEnterExclusive();
285
286 if ( !GlobalEvents )
287 {
288 GlobalEvents = ExAllocatePoolWithTag(PagedPool, sizeof(EVENTTABLE), TAG_HOOK);
289 if (GlobalEvents == NULL)
290 {
291 SetLastWin32Error(ERROR_NOT_ENOUGH_MEMORY);
292 goto SetEventExit;
293 }
294 GlobalEvents->Counts = 0;
295 InitializeListHead(&GlobalEvents->Events);
296 }
297
298 if (eventMin > eventMax)
299 {
300 SetLastWin32Error(ERROR_INVALID_HOOK_FILTER);
301 goto SetEventExit;
302 }
303
304 if (!lpfnWinEventProc)
305 {
306 SetLastWin32Error(ERROR_INVALID_FILTER_PROC);
307 goto SetEventExit;
308 }
309
310 if ((dwflags & WINEVENT_INCONTEXT) && !hmodWinEventProc)
311 {
312 SetLastWin32Error(ERROR_HOOK_NEEDS_HMOD);
313 goto SetEventExit;
314 }
315
316 if (idThread)
317 {
318 Status = PsLookupThreadByThreadId((HANDLE)(DWORD_PTR)idThread, &Thread);
319 if (!NT_SUCCESS(Status))
320 {
321 SetLastWin32Error(ERROR_INVALID_THREAD_ID);
322 goto SetEventExit;
323 }
324 }
325
326 pEH = UserCreateObject(gHandleTable, NULL, &Handle, otEvent, sizeof(EVENTHOOK));
327 if (pEH)
328 {
329 InsertTailList(&GlobalEvents->Events, &pEH->Chain);
330 GlobalEvents->Counts++;
331
332 UserHMGetHandle(pEH) = Handle;
333 if (Thread)
334 pEH->head.pti = Thread->Tcb.Win32Thread;
335 else
336 pEH->head.pti = GetW32ThreadInfo();
337 pEH->eventMin = eventMin;
338 pEH->eventMax = eventMax;
339 pEH->idProcess = idProcess;
340 pEH->idThread = idThread;
341 pEH->Flags = dwflags;
342
343 if (NULL != hmodWinEventProc)
344 {
345 pEH->offPfn = (ULONG_PTR)((char *)lpfnWinEventProc - (char *)hmodWinEventProc);
346 pEH->ihmod = (INT)hmodWinEventProc;
347 pEH->Proc = lpfnWinEventProc;
348 }
349 else
350 pEH->Proc = lpfnWinEventProc;
351
352 UserDereferenceObject(pEH);
353
354 Ret = Handle;
355 IntSetSrvEventMask( eventMin, eventMax);
356 }
357
358 SetEventExit:
359 if (Thread) ObDereferenceObject(Thread);
360 UserLeave();
361 return Ret;
362 }
363
364 BOOL
365 APIENTRY
366 NtUserUnhookWinEvent(
367 HWINEVENTHOOK hWinEventHook)
368 {
369 PEVENTHOOK pEH;
370 BOOL Ret = FALSE;
371
372 UserEnterExclusive();
373
374 pEH = (PEVENTHOOK)UserGetObject(gHandleTable, hWinEventHook, otEvent);
375 if (pEH)
376 {
377 Ret = IntRemoveEvent(pEH);
378 }
379
380 UserLeave();
381 return Ret;
382 }
383
384 /* EOF */