Sync with trunk r58687.
[reactos.git] / win32ss / user / ntuser / focus.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Focus functions
5 * FILE: subsystems/win32/win32k/ntuser/focus.c
6 * PROGRAMER: ReactOS Team
7 */
8
9 #include <win32k.h>
10 DBG_DEFAULT_CHANNEL(UserFocus);
11
12 PUSER_MESSAGE_QUEUE gpqForeground = NULL;
13 PUSER_MESSAGE_QUEUE gpqForegroundPrev = NULL;
14 PTHREADINFO gptiForeground = NULL;
15 PPROCESSINFO gppiLockSFW = NULL;
16 ULONG guSFWLockCount = 0; // Rule #8, No menus are active. So should be zero.
17 PTHREADINFO ptiLastInput = NULL;
18
19 /*
20 Check locking of a process or one or more menus are active.
21 */
22 BOOL FASTCALL
23 IsFGLocked(VOID)
24 {
25 return (gppiLockSFW || guSFWLockCount);
26 }
27
28 HWND FASTCALL
29 IntGetCaptureWindow(VOID)
30 {
31 PUSER_MESSAGE_QUEUE ForegroundQueue = IntGetFocusMessageQueue();
32 return ( ForegroundQueue ? (ForegroundQueue->spwndCapture ? UserHMGetHandle(ForegroundQueue->spwndCapture) : 0) : 0);
33 }
34
35 HWND FASTCALL
36 IntGetThreadFocusWindow(VOID)
37 {
38 PTHREADINFO pti;
39 PUSER_MESSAGE_QUEUE ThreadQueue;
40
41 pti = PsGetCurrentThreadWin32Thread();
42 ThreadQueue = pti->MessageQueue;
43 if (!ThreadQueue)
44 return NULL;
45 return ThreadQueue->spwndFocus ? UserHMGetHandle(ThreadQueue->spwndFocus) : 0;
46 }
47
48 BOOL FASTCALL
49 co_IntSendDeactivateMessages(HWND hWndPrev, HWND hWnd)
50 {
51 USER_REFERENCE_ENTRY RefPrev;
52 PWND WndPrev;
53 BOOL Ret = TRUE;
54
55 if (hWndPrev && (WndPrev = ValidateHwndNoErr(hWndPrev)))
56 {
57 UserRefObjectCo(WndPrev, &RefPrev);
58
59 if (co_IntSendMessageNoWait(hWndPrev, WM_NCACTIVATE, FALSE, 0)) //(LPARAM)hWnd))
60 {
61 co_IntSendMessageNoWait(hWndPrev, WM_ACTIVATE,
62 MAKEWPARAM(WA_INACTIVE, WndPrev->style & WS_MINIMIZE),
63 (LPARAM)hWnd);
64
65 if (WndPrev)
66 WndPrev->state &= ~WNDS_ACTIVEFRAME;
67 }
68 else
69 {
70 ERR("Application is keeping itself Active to prevent the change!\n");
71 Ret = FALSE;
72 }
73
74 UserDerefObjectCo(WndPrev);
75 }
76 return Ret;
77 }
78
79 BOOL FASTCALL
80 co_IntMakeWindowActive(PWND Window)
81 {
82 PWND spwndOwner;
83 if (Window)
84 { // Set last active for window and it's owner.
85 spwndOwner = Window;
86 while (spwndOwner->spwndOwner)
87 {
88 spwndOwner = spwndOwner->spwndOwner;
89 }
90 spwndOwner->spwndLastActive = Window;
91 return TRUE;
92 }
93 ERR("MakeWindowActive Failed!\n");
94 return FALSE;
95 }
96
97 BOOL FASTCALL
98 co_IntSendActivateMessages(PWND WindowPrev, PWND Window, BOOL MouseActivate, BOOL Async)
99 {
100 USER_REFERENCE_ENTRY Ref, RefPrev;
101 HANDLE OldTID, NewTID;
102 PTHREADINFO pti, ptiOld, ptiNew;
103 BOOL InAAPM = FALSE;
104
105 if (Window)
106 {
107 pti = PsGetCurrentThreadWin32Thread();
108
109 UserRefObjectCo(Window, &Ref);
110
111 if (WindowPrev) UserRefObjectCo(WindowPrev, &RefPrev);
112
113 /* Send palette messages */
114 if (gpsi->PUSIFlags & PUSIF_PALETTEDISPLAY &&
115 co_IntPostOrSendMessage(UserHMGetHandle(Window), WM_QUERYNEWPALETTE, 0, 0))
116 {
117 UserSendNotifyMessage( HWND_BROADCAST,
118 WM_PALETTEISCHANGING,
119 (WPARAM)UserHMGetHandle(Window),
120 0);
121 }
122 //// Fixes bug 7089.
123 if (!(Window->style & WS_CHILD))
124 {
125 PWND pwndTemp = co_GetDesktopWindow(Window)->spwndChild;
126
127 while (pwndTemp && !(pwndTemp->style & WS_VISIBLE)) pwndTemp = pwndTemp->spwndNext;
128
129 if (Window != pwndTemp || (WindowPrev && !IntIsWindowVisible(WindowPrev)))
130 {
131 if (!Async || pti->MessageQueue == gpqForeground)
132 {
133 UINT flags = SWP_NOSIZE | SWP_NOMOVE;
134 if (Window == pwndTemp) flags |= SWP_NOACTIVATE;
135 co_WinPosSetWindowPos(Window, HWND_TOP, 0, 0, 0, 0, flags);
136 }
137 }
138 }
139 ////
140 OldTID = WindowPrev ? IntGetWndThreadId(WindowPrev) : NULL;
141 NewTID = IntGetWndThreadId(Window);
142 ptiOld = WindowPrev ? WindowPrev->head.pti : NULL;
143 ptiNew = Window->head.pti;
144
145 //ERR("SendActivateMessage Old -> %x, New -> %x\n", OldTID, NewTID);
146
147 if (!(pti->TIF_flags & TIF_INACTIVATEAPPMSG) &&
148 (!WindowPrev || OldTID != NewTID) )
149 {
150 PWND cWindow;
151 HWND *List, *phWnd;
152
153 List = IntWinListChildren(UserGetDesktopWindow());
154 if ( List )
155 {
156 if ( OldTID )
157 {
158 ptiOld->TIF_flags |= TIF_INACTIVATEAPPMSG;
159 ptiOld->pClientInfo->dwTIFlags = ptiOld->TIF_flags;
160
161 for (phWnd = List; *phWnd; ++phWnd)
162 {
163 cWindow = ValidateHwndNoErr(*phWnd);
164 if (cWindow && cWindow->head.pti == ptiOld)
165 { // FALSE if the window is being deactivated,
166 // ThreadId that owns the window being activated.
167 co_IntSendMessageNoWait(*phWnd, WM_ACTIVATEAPP, FALSE, (LPARAM)NewTID);
168 }
169 }
170 ptiOld->TIF_flags &= ~TIF_INACTIVATEAPPMSG;
171 ptiOld->pClientInfo->dwTIFlags = ptiOld->TIF_flags;
172 }
173 if ( NewTID )
174 { //// Prevents a resource crash due to reentrance!
175 InAAPM = TRUE;
176 pti->TIF_flags |= TIF_INACTIVATEAPPMSG;
177 pti->pClientInfo->dwTIFlags = pti->TIF_flags;
178 ////
179 for (phWnd = List; *phWnd; ++phWnd)
180 {
181 cWindow = ValidateHwndNoErr(*phWnd);
182 if (cWindow && cWindow->head.pti == ptiNew)
183 { // TRUE if the window is being activated,
184 // ThreadId that owns the window being deactivated.
185 co_IntSendMessageNoWait(*phWnd, WM_ACTIVATEAPP, TRUE, (LPARAM)OldTID);
186 }
187 }
188 }
189 ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
190 }
191 }
192 if (WindowPrev)
193 UserDerefObjectCo(WindowPrev); // Now allow the previous window to die.
194
195 if (Window->state & WNDS_ACTIVEFRAME)
196 { // If already active frame do not allow NCPaint.
197 //ERR("SendActivateMessage Is Active Frame!\n");
198 Window->state |= WNDS_NONCPAINT;
199 }
200
201 if (Window->style & WS_MINIMIZE)
202 {
203 TRACE("Widow was minimized\n");
204 }
205
206 co_IntMakeWindowActive(Window);
207
208 UserDerefObjectCo(Window);
209
210 /* FIXME: IntIsWindow */
211 co_IntSendMessageNoWait( UserHMGetHandle(Window),
212 WM_NCACTIVATE,
213 (WPARAM)(Window == gpqForeground->spwndActive),
214 0); //(LPARAM)hWndPrev);
215
216 co_IntSendMessageNoWait( UserHMGetHandle(Window),
217 WM_ACTIVATE,
218 MAKEWPARAM(MouseActivate ? WA_CLICKACTIVE : WA_ACTIVE, Window->style & WS_MINIMIZE),
219 (LPARAM)(WindowPrev ? UserHMGetHandle(WindowPrev) : 0));
220
221 if (!Window->spwndOwner && !IntGetParent(Window))
222 {
223 // FIXME lParam; The value is TRUE if the window is in full-screen mode, or FALSE otherwise.
224 co_IntShellHookNotify(HSHELL_WINDOWACTIVATED, (WPARAM) UserHMGetHandle(Window), FALSE);
225 }
226
227 Window->state &= ~WNDS_NONCPAINT;
228
229 }
230 return InAAPM;
231 }
232
233 VOID FASTCALL
234 IntSendFocusMessages( PTHREADINFO pti, PWND pWnd)
235 {
236 PWND pWndPrev;
237 PUSER_MESSAGE_QUEUE ThreadQueue = pti->MessageQueue; // Queue can change...
238
239 ThreadQueue->QF_flags &= ~QF_FOCUSNULLSINCEACTIVE;
240 if (!pWnd && ThreadQueue->spwndActive)
241 {
242 ThreadQueue->QF_flags |= QF_FOCUSNULLSINCEACTIVE;
243 }
244
245 pWndPrev = ThreadQueue->spwndFocus;
246
247 /* check if the specified window can be set in the input data of a given queue */
248 if (!pWnd || ThreadQueue == pWnd->head.pti->MessageQueue)
249 /* set the current thread focus window */
250 ThreadQueue->spwndFocus = pWnd;
251
252 if (pWnd)
253 {
254 if (pWndPrev)
255 {
256 co_IntPostOrSendMessage(UserHMGetHandle(pWndPrev), WM_KILLFOCUS, (WPARAM)UserHMGetHandle(pWnd), 0);
257 }
258 if (ThreadQueue->spwndFocus == pWnd)
259 {
260 IntNotifyWinEvent(EVENT_OBJECT_FOCUS, pWnd, OBJID_CLIENT, CHILDID_SELF, 0);
261 co_IntPostOrSendMessage(UserHMGetHandle(pWnd), WM_SETFOCUS, (WPARAM)(pWndPrev ? UserHMGetHandle(pWndPrev) : NULL), 0);
262 }
263 }
264 else
265 {
266 if (pWndPrev)
267 {
268 IntNotifyWinEvent(EVENT_OBJECT_FOCUS, NULL, OBJID_CLIENT, CHILDID_SELF, 0);
269 co_IntPostOrSendMessage(UserHMGetHandle(pWndPrev), WM_KILLFOCUS, 0, 0);
270 }
271 }
272 }
273
274 HWND FASTCALL
275 IntFindChildWindowToOwner(PWND Root, PWND Owner)
276 {
277 HWND Ret;
278 PWND Child, OwnerWnd;
279
280 for(Child = Root->spwndChild; Child; Child = Child->spwndNext)
281 {
282 OwnerWnd = Child->spwndOwner;
283 if(!OwnerWnd)
284 continue;
285
286 if(OwnerWnd == Owner)
287 {
288 Ret = Child->head.h;
289 return Ret;
290 }
291 }
292
293 return NULL;
294 }
295
296 VOID FASTCALL
297 FindRemoveAsyncMsg(PWND Wnd)
298 {
299 PUSER_MESSAGE_QUEUE MessageQueue;
300 PUSER_SENT_MESSAGE Message;
301 PLIST_ENTRY Entry;
302
303 if (!Wnd) return;
304
305 MessageQueue = Wnd->head.pti->MessageQueue;
306
307 if (!IsListEmpty(&MessageQueue->SentMessagesListHead))
308 {
309 // Scan sent queue messages to see if we received async messages.
310 Entry = MessageQueue->SentMessagesListHead.Flink;
311 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
312 do
313 {
314 if (Message->Msg.message == WM_ASYNC_SETACTIVEWINDOW &&
315 Message->Msg.hwnd == UserHMGetHandle(Wnd) &&
316 Message->Msg.wParam == 0 )
317 {
318 TRACE("ASYNC SAW: Found one in the Sent Msg Queue! %p\n", Message->Msg.hwnd);
319 RemoveEntryList(Entry); // Purge the entry.
320 }
321 Entry = Message->ListEntry.Flink;
322 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
323 }
324 while (Entry != &MessageQueue->SentMessagesListHead);
325 }
326 }
327
328 /*
329 Can the system force foreground from one or more conditions.
330 */
331 BOOL FASTCALL
332 CanForceFG(PPROCESSINFO ppi)
333 {
334 if (!ptiLastInput ||
335 ptiLastInput->ppi == ppi ||
336 !gptiForeground ||
337 gptiForeground->ppi == ppi ||
338 ppi->W32PF_flags & (W32PF_ALLOWFOREGROUNDACTIVATE | W32PF_SETFOREGROUNDALLOWED) ||
339 gppiInputProvider == ppi ||
340 !gpqForeground
341 ) return TRUE;
342 return FALSE;
343 }
344
345 /*
346 MSDN:
347 The system restricts which processes can set the foreground window. A process
348 can set the foreground window only if one of the following conditions is true:
349
350 * The process is the foreground process.
351 * The process was started by the foreground process.
352 * The process received the last input event.
353 * There is no foreground process.
354 * The foreground process is being debugged.
355 * The foreground is not locked (see LockSetForegroundWindow).
356 * The foreground lock time-out has expired (see SPI_GETFOREGROUNDLOCKTIMEOUT in SystemParametersInfo).
357 * No menus are active.
358 */
359
360 static BOOL FASTCALL
361 co_IntSetForegroundAndFocusWindow(
362 _In_ PWND Wnd,
363 _In_ BOOL MouseActivate)
364 {
365 HWND hWnd;
366 HWND hWndPrev = NULL;
367 PUSER_MESSAGE_QUEUE PrevForegroundQueue;
368 PTHREADINFO pti;
369 BOOL fgRet = FALSE, Ret = FALSE;
370
371 ASSERT_REFS_CO(Wnd);
372 NT_ASSERT(Wnd != NULL);
373
374 hWnd = UserHMGetHandle(Wnd);
375
376 TRACE("SetForegroundAndFocusWindow(%x, %s)\n", hWnd, (MouseActivate ? "TRUE" : "FALSE"));
377
378 PrevForegroundQueue = IntGetFocusMessageQueue(); // Use this active desktop.
379 pti = PsGetCurrentThreadWin32Thread();
380
381 if (PrevForegroundQueue)
382 { // Same Window Q as foreground just do active.
383 //ERR("Same Window Q as foreground just do active.\n");
384 if (Wnd && Wnd->head.pti->MessageQueue == PrevForegroundQueue)
385 {
386 if (pti->MessageQueue == PrevForegroundQueue)
387 { // Same WQ and TQ go active.
388 //ERR("Same WQ and TQ go active.\n");
389 Ret = co_IntSetActiveWindow(Wnd, NULL, MouseActivate, TRUE, FALSE);
390 }
391 else if (Wnd->head.pti->MessageQueue->spwndActive == Wnd)
392 { // Same WQ and it is active.
393 //ERR("Same WQ and it is active.\n");
394 Ret = TRUE;
395 }
396 else
397 { // Same WQ as FG but not the same TQ send active.
398 //ERR("Same WQ as FG but not the same TQ send active.\n");
399 co_IntSendMessageNoWait(hWnd, WM_ASYNC_SETACTIVEWINDOW, (WPARAM)Wnd, (LPARAM)MouseActivate );
400 Ret = TRUE;
401 }
402 return Ret;
403 }
404
405 hWndPrev = PrevForegroundQueue->spwndActive ? UserHMGetHandle(PrevForegroundQueue->spwndActive) : 0;
406 }
407
408 if ( (( !IsFGLocked() || pti->ppi == gppiInputProvider ) &&
409 ( CanForceFG(pti->ppi) || pti->TIF_flags & (TIF_SYSTEMTHREAD|TIF_CSRSSTHREAD|TIF_ALLOWFOREGROUNDACTIVATE) )) ||
410 pti->ppi == ppiScrnSaver
411 )
412 {
413 IntSetFocusMessageQueue(Wnd->head.pti->MessageQueue);
414 gptiForeground = Wnd->head.pti;
415 TRACE("Set Foreground pti 0x%p Q 0x%p\n",Wnd->head.pti, Wnd->head.pti->MessageQueue);
416 /*
417 Henri Verbeet,
418 What happens is that we get the WM_WINE_SETACTIVEWINDOW message sent by the
419 other thread after we already changed the foreground window back to our own
420 window.
421 */
422 FindRemoveAsyncMsg(Wnd); // Do this to fix test_SFW todos!
423 fgRet = TRUE;
424 }
425
426 // Fix FG Bounce with regedit.
427 if (hWndPrev != hWnd )
428 {
429 if (PrevForegroundQueue &&
430 fgRet &&
431 Wnd->head.pti->MessageQueue != PrevForegroundQueue &&
432 PrevForegroundQueue->spwndActive)
433 {
434 //ERR("SFGW: Send NULL to 0x%x\n",hWndPrev);
435 if (pti->MessageQueue == PrevForegroundQueue)
436 {
437 //ERR("SFGW: TI same as Prev TI\n");
438 co_IntSetActiveWindow(NULL, NULL, FALSE, TRUE, FALSE);
439 }
440 else
441 co_IntSendMessageNoWait(hWndPrev, WM_ASYNC_SETACTIVEWINDOW, 0, 0 );
442 }
443 }
444
445 if (pti->MessageQueue == Wnd->head.pti->MessageQueue)
446 {
447 Ret = co_IntSetActiveWindow(Wnd, NULL, MouseActivate, TRUE, FALSE);
448 }
449 else if (Wnd->head.pti->MessageQueue->spwndActive == Wnd)
450 {
451 Ret = TRUE;
452 }
453 else
454 {
455 co_IntSendMessageNoWait(hWnd, WM_ASYNC_SETACTIVEWINDOW, (WPARAM)Wnd, (LPARAM)MouseActivate );
456 Ret = TRUE;
457 }
458
459 return Ret && fgRet;
460 }
461
462 BOOL FASTCALL
463 co_IntMouseActivateWindow(PWND Wnd)
464 {
465 HWND Top;
466 PWND TopWindow;
467 USER_REFERENCE_ENTRY Ref;
468
469 ASSERT_REFS_CO(Wnd);
470
471 if (Wnd->style & WS_DISABLED)
472 {
473 BOOL Ret;
474 PWND TopWnd;
475 PWND DesktopWindow = UserGetDesktopWindow();
476 if (DesktopWindow)
477 {
478 Top = IntFindChildWindowToOwner(DesktopWindow, Wnd);
479 if ((TopWnd = ValidateHwndNoErr(Top)))
480 {
481 UserRefObjectCo(TopWnd, &Ref);
482 Ret = co_IntMouseActivateWindow(TopWnd);
483 UserDerefObjectCo(TopWnd);
484
485 return Ret;
486 }
487 }
488 return FALSE;
489 }
490
491 TopWindow = UserGetAncestor(Wnd, GA_ROOT);
492 //if (TopWindow) {ERR("MAW 2 pWnd %p hWnd %p\n",TopWindow,TopWindow->head.h);}
493 if (!TopWindow) return FALSE;
494
495 /* TMN: Check return value from this function? */
496 UserRefObjectCo(TopWindow, &Ref);
497 co_IntSetForegroundAndFocusWindow(TopWindow, TRUE);
498 UserDerefObjectCo(TopWindow);
499 return TRUE;
500 }
501
502 BOOL FASTCALL
503 co_IntSetActiveWindow(PWND Wnd OPTIONAL, HWND * Prev, BOOL bMouse, BOOL bFocus, BOOL Async)
504 {
505 PTHREADINFO pti;
506 PUSER_MESSAGE_QUEUE ThreadQueue;
507 PWND pWndChg, WndPrev; // State changes.
508 HWND hWndPrev;
509 HWND hWnd = 0;
510 BOOL InAAPM;
511 CBTACTIVATESTRUCT cbt;
512
513 if (Wnd)
514 {
515 ASSERT_REFS_CO(Wnd);
516 hWnd = UserHMGetHandle(Wnd);
517 if ((Wnd->style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return FALSE;
518 if (Wnd == UserGetDesktopWindow()) return FALSE;
519 }
520
521 pti = PsGetCurrentThreadWin32Thread();
522 ThreadQueue = pti->MessageQueue;
523 ASSERT(ThreadQueue != 0);
524
525 hWndPrev = ThreadQueue->spwndActive ? UserHMGetHandle(ThreadQueue->spwndActive) : NULL;
526 if (Prev) *Prev = hWndPrev;
527 if (hWndPrev == hWnd) return TRUE;
528
529 pWndChg = ThreadQueue->spwndActive; // Keep to notify of a preemptive switch.
530
531 if (Wnd)
532 {
533 if (ThreadQueue != Wnd->head.pti->MessageQueue)
534 {
535 PUSER_MESSAGE_QUEUE ForegroundQueue = IntGetFocusMessageQueue();
536 // Rule 1 & 4, We are foreground so set this FG window or NULL foreground....
537 if (!ForegroundQueue || ForegroundQueue == ThreadQueue)
538 {
539 return co_IntSetForegroundAndFocusWindow(Wnd, bMouse);
540 }
541 }
542
543 if (Wnd->state & WNDS_BEINGACTIVATED) return TRUE;
544 }
545
546 /* Call CBT hook chain */
547 cbt.fMouse = bMouse;
548 cbt.hWndActive = hWndPrev;
549 if (co_HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hWnd, (LPARAM)&cbt))
550 {
551 ERR("SetActiveWindow: WH_CBT Call Hook return!\n");
552 return FALSE;
553 }
554
555 if ( ThreadQueue->spwndActive && ThreadQueue->spwndActive->state & WNDS_DESTROYED )
556 ThreadQueue->spwndActive = NULL;
557 else
558 ThreadQueue->spwndActivePrev = ThreadQueue->spwndActive;
559
560 WndPrev = ThreadQueue->spwndActive; // Keep to save changing active.
561
562 if (WndPrev)
563 {
564 if (ThreadQueue == gpqForeground) gpqForegroundPrev = ThreadQueue;
565 if (!co_IntSendDeactivateMessages(hWndPrev, hWnd)) return FALSE;
566 }
567
568 // While in calling message proc or hook:
569 // Fail if a preemptive switch was made, current active not made previous,
570 // focus window is dead or no longer the same thread queue.
571 if ( ThreadQueue->spwndActivePrev != ThreadQueue->spwndActive ||
572 pWndChg != ThreadQueue->spwndActive ||
573 (Wnd && !VerifyWnd(Wnd)) ||
574 ThreadQueue != pti->MessageQueue )
575 {
576 ERR("SetActiveWindow: Summery ERROR, active state changed!\n");
577 return FALSE;
578 }
579
580 if (!WndPrev) ThreadQueue->QF_flags &= ~QF_FOCUSNULLSINCEACTIVE;
581
582 if (Wnd) Wnd->state |= WNDS_BEINGACTIVATED;
583
584 IntNotifyWinEvent(EVENT_SYSTEM_FOREGROUND, Wnd, OBJID_WINDOW, CHILDID_SELF, WEF_SETBYWNDPTI);
585
586 /* check if the specified window can be set in the input data of a given queue */
587 if ( !Wnd || ThreadQueue == Wnd->head.pti->MessageQueue)
588 {
589 /* set the current thread active window */
590 ThreadQueue->spwndActive = Wnd;
591 }
592
593 InAAPM = co_IntSendActivateMessages(WndPrev, Wnd, bMouse, Async);
594
595 /* now change focus if necessary */
596 if (bFocus && !(ThreadQueue->QF_flags & QF_FOCUSNULLSINCEACTIVE))
597 {
598 /* Do not change focus if the window is no longer active */
599 if (ThreadQueue->spwndActive == Wnd)
600 {
601 if (!ThreadQueue->spwndFocus ||
602 !Wnd ||
603 UserGetAncestor(ThreadQueue->spwndFocus, GA_ROOT) != Wnd)
604 {
605 co_UserSetFocus(Wnd);
606 }
607 }
608 }
609
610 if (InAAPM)
611 {
612 pti->TIF_flags &= ~TIF_INACTIVATEAPPMSG;
613 pti->pClientInfo->dwTIFlags = pti->TIF_flags;
614 }
615
616 // FIXME: Used in the menu loop!!!
617 //ThreadQueue->QF_flags |= QF_ACTIVATIONCHANGE;
618
619 if (Wnd) Wnd->state &= ~WNDS_BEINGACTIVATED;
620 return (ThreadQueue->spwndActive == Wnd);
621 }
622
623 HWND FASTCALL
624 co_UserSetFocus(PWND Window)
625 {
626 HWND hWndPrev = 0;
627 PWND pwndTop;
628 PTHREADINFO pti;
629 PUSER_MESSAGE_QUEUE ThreadQueue;
630
631 if (Window)
632 ASSERT_REFS_CO(Window);
633
634 pti = PsGetCurrentThreadWin32Thread();
635 ThreadQueue = pti->MessageQueue;
636 ASSERT(ThreadQueue != 0);
637
638 TRACE("Enter SetFocus hWnd 0x%p pti 0x%p\n",Window ? UserHMGetHandle(Window) : 0, pti );
639
640 hWndPrev = ThreadQueue->spwndFocus ? UserHMGetHandle(ThreadQueue->spwndFocus) : 0;
641
642 if (Window != 0)
643 {
644 if (hWndPrev == UserHMGetHandle(Window))
645 {
646 return hWndPrev ? (IntIsWindow(hWndPrev) ? hWndPrev : 0) : 0; /* Nothing to do */
647 }
648
649 if (Window->head.pti->MessageQueue != ThreadQueue)
650 {
651 ERR("SetFocus Must have the same Q!\n");
652 return 0;
653 }
654
655 /* Check if we can set the focus to this window */
656 pwndTop = Window;
657 for (;;)
658 {
659 if (pwndTop->style & (WS_MINIMIZED|WS_DISABLED)) return 0;
660 if (!pwndTop->spwndParent || pwndTop->spwndParent == UserGetDesktopWindow())
661 {
662 if ((pwndTop->style & (WS_POPUP|WS_CHILD)) == WS_CHILD) return 0;
663 break;
664 }
665 if (pwndTop->spwndParent == UserGetMessageWindow()) return 0;
666 pwndTop = pwndTop->spwndParent;
667 }
668
669 if (co_HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)Window->head.h, (LPARAM)hWndPrev))
670 {
671 ERR("SetFocus 1 WH_CBT Call Hook return!\n");
672 return 0;
673 }
674
675 /* Activate pwndTop if needed. */
676 if (pwndTop != ThreadQueue->spwndActive)
677 {
678 PUSER_MESSAGE_QUEUE ForegroundQueue = IntGetFocusMessageQueue(); // Keep it based on desktop.
679 if (ThreadQueue != ForegroundQueue) // HACK see rule 2 & 3.
680 {
681 if (!co_IntSetForegroundAndFocusWindow(pwndTop, FALSE))
682 {
683 ERR("SetFocus: Set Foreground and Focus Failed!\n");
684 return 0;
685 }
686 }
687
688 /* Set Active when it is needed. */
689 if (pwndTop != ThreadQueue->spwndActive)
690 {
691 if (!co_IntSetActiveWindow(pwndTop, NULL, FALSE, FALSE, FALSE))
692 {
693 ERR("SetFocus: Set Active Failed!\n");
694 return 0;
695 }
696 }
697
698 /* Abort if window destroyed */
699 if (Window->state2 & WNDS2_INDESTROY) return 0;
700 /* Do not change focus if the window is no longer active */
701 if (pwndTop != ThreadQueue->spwndActive)
702 {
703 ERR("SetFocus: Top window did not go active!\n");
704 return 0;
705 }
706 }
707
708 // Check again! SetActiveWindow could have set the focus via WM_ACTIVATE.
709 hWndPrev = ThreadQueue->spwndFocus ? UserHMGetHandle(ThreadQueue->spwndFocus) : 0;
710
711 IntSendFocusMessages( pti, Window);
712
713 TRACE("Focus: %d -> %d\n", hWndPrev, Window->head.h);
714 }
715 else /* NULL hwnd passed in */
716 {
717 // if (!hWndPrev) return 0; /* nothing to do */
718
719 if (co_HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)0, (LPARAM)hWndPrev))
720 {
721 ERR("SetFocus: 2 WH_CBT Call Hook return!\n");
722 return 0;
723 }
724
725 /* set the current thread focus window null */
726 IntSendFocusMessages( pti, NULL);
727 }
728 return hWndPrev ? (IntIsWindow(hWndPrev) ? hWndPrev : 0) : 0;
729 }
730
731 HWND FASTCALL
732 UserGetForegroundWindow(VOID)
733 {
734 PUSER_MESSAGE_QUEUE ForegroundQueue;
735
736 ForegroundQueue = IntGetFocusMessageQueue();
737 return( ForegroundQueue ? (ForegroundQueue->spwndActive ? UserHMGetHandle(ForegroundQueue->spwndActive) : 0) : 0);
738 }
739
740 HWND FASTCALL UserGetActiveWindow(VOID)
741 {
742 PTHREADINFO pti;
743 PUSER_MESSAGE_QUEUE ThreadQueue;
744
745 pti = PsGetCurrentThreadWin32Thread();
746 ThreadQueue = pti->MessageQueue;
747 return( ThreadQueue ? (ThreadQueue->spwndActive ? UserHMGetHandle(ThreadQueue->spwndActive) : 0) : 0);
748 }
749
750 HWND APIENTRY
751 IntGetCapture(VOID)
752 {
753 PTHREADINFO pti;
754 PUSER_MESSAGE_QUEUE ThreadQueue;
755 DECLARE_RETURN(HWND);
756
757 TRACE("Enter IntGetCapture\n");
758
759 pti = PsGetCurrentThreadWin32Thread();
760 ThreadQueue = pti->MessageQueue;
761 RETURN( ThreadQueue ? (ThreadQueue->spwndCapture ? UserHMGetHandle(ThreadQueue->spwndCapture) : 0) : 0);
762
763 CLEANUP:
764 TRACE("Leave IntGetCapture, ret=%i\n",_ret_);
765 END_CLEANUP;
766 }
767
768 HWND FASTCALL
769 co_UserSetCapture(HWND hWnd)
770 {
771 PTHREADINFO pti;
772 PUSER_MESSAGE_QUEUE ThreadQueue;
773 PWND pWnd, Window = NULL;
774 HWND hWndPrev;
775
776 pti = PsGetCurrentThreadWin32Thread();
777 ThreadQueue = pti->MessageQueue;
778
779 if (ThreadQueue->QF_flags & QF_CAPTURELOCKED)
780 return NULL;
781
782 if (hWnd && (Window = UserGetWindowObject(hWnd)))
783 {
784 if (Window->head.pti->MessageQueue != ThreadQueue)
785 {
786 return NULL;
787 }
788 }
789
790 hWndPrev = MsqSetStateWindow(ThreadQueue, MSQ_STATE_CAPTURE, hWnd);
791
792 if (hWndPrev)
793 {
794 pWnd = UserGetWindowObject(hWndPrev);
795 if (pWnd)
796 IntNotifyWinEvent(EVENT_SYSTEM_CAPTUREEND, pWnd, OBJID_WINDOW, CHILDID_SELF, WEF_SETBYWNDPTI);
797 }
798
799 if (Window)
800 IntNotifyWinEvent(EVENT_SYSTEM_CAPTURESTART, Window, OBJID_WINDOW, CHILDID_SELF, WEF_SETBYWNDPTI);
801
802 if (hWndPrev && hWndPrev != hWnd)
803 {
804 if (ThreadQueue->MenuOwner && Window) ThreadQueue->QF_flags |= QF_CAPTURELOCKED;
805
806 co_IntPostOrSendMessage(hWndPrev, WM_CAPTURECHANGED, 0, (LPARAM)hWnd);
807
808 ThreadQueue->QF_flags &= ~QF_CAPTURELOCKED;
809 }
810
811 ThreadQueue->spwndCapture = Window;
812
813 if (hWnd == NULL) // Release mode.
814 {
815 MOUSEINPUT mi;
816 /// These are HACKS!
817 /* Also remove other windows if not capturing anymore */
818 MsqSetStateWindow(ThreadQueue, MSQ_STATE_MENUOWNER, NULL);
819 MsqSetStateWindow(ThreadQueue, MSQ_STATE_MOVESIZE, NULL);
820 ///
821 /* Somebody may have missed some mouse movements */
822 mi.dx = 0;
823 mi.dy = 0;
824 mi.mouseData = 0;
825 mi.dwFlags = MOUSEEVENTF_MOVE;
826 mi.time = 0;
827 mi.dwExtraInfo = 0;
828 UserSendMouseInput(&mi, FALSE);
829 }
830 return hWndPrev;
831 }
832
833 /*
834 API Call
835 */
836 BOOL
837 FASTCALL
838 IntReleaseCapture(VOID)
839 {
840 PTHREADINFO pti;
841 PUSER_MESSAGE_QUEUE ThreadQueue;
842
843 pti = PsGetCurrentThreadWin32Thread();
844 ThreadQueue = pti->MessageQueue;
845
846 // Can not release inside WM_CAPTURECHANGED!!
847 if (ThreadQueue->QF_flags & QF_CAPTURELOCKED) return FALSE;
848
849 co_UserSetCapture(NULL);
850
851 return TRUE;
852 }
853
854 /*
855 API Call
856 */
857 BOOL FASTCALL
858 co_IntSetForegroundWindow(PWND Window)
859 {
860 ASSERT_REFS_CO(Window);
861
862 return co_IntSetForegroundAndFocusWindow(Window, FALSE);
863 }
864
865 /*
866 API Call
867 */
868 BOOL FASTCALL
869 co_IntSetForegroundWindowMouse(PWND Window)
870 {
871 if (Window) ASSERT_REFS_CO(Window);
872
873 return co_IntSetForegroundAndFocusWindow(Window, TRUE);
874 }
875
876 /*
877 API Call
878 */
879 BOOL FASTCALL
880 IntLockSetForegroundWindow(UINT uLockCode)
881 {
882 ULONG Err = ERROR_ACCESS_DENIED;
883 PPROCESSINFO ppi = PsGetCurrentProcessWin32Process();
884 switch (uLockCode)
885 {
886 case LSFW_LOCK:
887 if ( CanForceFG(ppi) && !gppiLockSFW )
888 {
889 gppiLockSFW = ppi;
890 return TRUE;
891 }
892 break;
893 case LSFW_UNLOCK:
894 if ( gppiLockSFW == ppi)
895 {
896 gppiLockSFW = NULL;
897 return TRUE;
898 }
899 break;
900 default:
901 Err = ERROR_INVALID_PARAMETER;
902 }
903 EngSetLastError(Err);
904 return FALSE;
905 }
906
907 /*
908 API Call
909 */
910 BOOL FASTCALL
911 IntAllowSetForegroundWindow(DWORD dwProcessId)
912 {
913 PPROCESSINFO ppi, ppiCur;
914 PEPROCESS Process = NULL;
915
916 ppi = NULL;
917 if (dwProcessId != ASFW_ANY)
918 {
919 if (!NT_SUCCESS(PsLookupProcessByProcessId((HANDLE)dwProcessId, &Process)))
920 {
921 EngSetLastError(ERROR_INVALID_PARAMETER);
922 return FALSE;
923 }
924 ppi = PsGetProcessWin32Process(Process);
925 if (!ppi)
926 {
927 ObDereferenceObject(Process);
928 return FALSE;
929 }
930 }
931 ppiCur = PsGetCurrentProcessWin32Process();
932 if (!CanForceFG(ppiCur))
933 {
934 if (Process) ObDereferenceObject(Process);
935 EngSetLastError(ERROR_ACCESS_DENIED);
936 return FALSE;
937 }
938 if (dwProcessId == ASFW_ANY)
939 { // All processes will be enabled to set the foreground window.
940 ptiLastInput = NULL;
941 }
942 else
943 { // Rule #3, last input event in force.
944 ERR("Fixme: ptiLastInput is SET!!\n");
945 //ptiLastInput = ppi->ptiList;
946 ObDereferenceObject(Process);
947 }
948 return TRUE;
949 }
950
951 /*
952 * @implemented
953 */
954 HWND APIENTRY
955 NtUserGetForegroundWindow(VOID)
956 {
957 DECLARE_RETURN(HWND);
958
959 TRACE("Enter NtUserGetForegroundWindow\n");
960 UserEnterExclusive();
961
962 RETURN( UserGetForegroundWindow());
963
964 CLEANUP:
965 TRACE("Leave NtUserGetForegroundWindow, ret=%i\n",_ret_);
966 UserLeave();
967 END_CLEANUP;
968 }
969
970 HWND APIENTRY
971 NtUserSetActiveWindow(HWND hWnd)
972 {
973 USER_REFERENCE_ENTRY Ref;
974 HWND hWndPrev;
975 PWND Window;
976 DECLARE_RETURN(HWND);
977
978 TRACE("Enter NtUserSetActiveWindow(%x)\n", hWnd);
979 UserEnterExclusive();
980
981 Window = NULL;
982 if (hWnd)
983 {
984 if (!(Window = UserGetWindowObject(hWnd)))
985 {
986 ERR("NtUserSetActiveWindow: Invalid handle 0x%p!\n",hWnd);
987 RETURN( NULL);
988 }
989 }
990
991 if (!Window ||
992 Window->head.pti->MessageQueue == gptiCurrent->MessageQueue)
993 {
994 hWndPrev = gptiCurrent->MessageQueue->spwndActive ? UserHMGetHandle(gptiCurrent->MessageQueue->spwndActive) : NULL;
995 if (Window) UserRefObjectCo(Window, &Ref);
996 co_IntSetActiveWindow(Window, NULL, FALSE, TRUE, FALSE);
997 if (Window) UserDerefObjectCo(Window);
998 RETURN( hWndPrev ? (IntIsWindow(hWndPrev) ? hWndPrev : 0) : 0 );
999 }
1000 RETURN( NULL);
1001
1002 CLEANUP:
1003 TRACE("Leave NtUserSetActiveWindow, ret=%p\n",_ret_);
1004 UserLeave();
1005 END_CLEANUP;
1006 }
1007
1008 /*
1009 * @implemented
1010 */
1011 HWND APIENTRY
1012 NtUserSetCapture(HWND hWnd)
1013 {
1014 DECLARE_RETURN(HWND);
1015
1016 TRACE("Enter NtUserSetCapture(%x)\n", hWnd);
1017 UserEnterExclusive();
1018
1019 RETURN( co_UserSetCapture(hWnd));
1020
1021 CLEANUP:
1022 TRACE("Leave NtUserSetCapture, ret=%i\n",_ret_);
1023 UserLeave();
1024 END_CLEANUP;
1025 }
1026
1027 /*
1028 * @implemented
1029 */
1030 HWND APIENTRY
1031 NtUserSetFocus(HWND hWnd)
1032 {
1033 PWND Window;
1034 USER_REFERENCE_ENTRY Ref;
1035 DECLARE_RETURN(HWND);
1036 HWND ret;
1037
1038 TRACE("Enter NtUserSetFocus(%x)\n", hWnd);
1039 UserEnterExclusive();
1040
1041 if (hWnd)
1042 {
1043 if (!(Window = UserGetWindowObject(hWnd)))
1044 {
1045 ERR("NtUserSetFocus: Invalid handle 0x%p!\n",hWnd);
1046 RETURN(NULL);
1047 }
1048
1049 UserRefObjectCo(Window, &Ref);
1050 ret = co_UserSetFocus(Window);
1051 UserDerefObjectCo(Window);
1052
1053 RETURN(ret);
1054 }
1055 else
1056 {
1057 RETURN( co_UserSetFocus(0));
1058 }
1059
1060 CLEANUP:
1061 TRACE("Leave NtUserSetFocus, ret=%p\n",_ret_);
1062 UserLeave();
1063 END_CLEANUP;
1064 }
1065
1066 /* EOF */