[user32]
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / focus.c
1 /*
2 * ReactOS Win32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 */
20
21 #include <win32k.h>
22
23 #define NDEBUG
24 #include <debug.h>
25
26 HWND FASTCALL
27 IntGetCaptureWindow(VOID)
28 {
29 PUSER_MESSAGE_QUEUE ForegroundQueue = IntGetFocusMessageQueue();
30 return ForegroundQueue != NULL ? ForegroundQueue->CaptureWindow : 0;
31 }
32
33 HWND FASTCALL
34 IntGetFocusWindow(VOID)
35 {
36 PUSER_MESSAGE_QUEUE ForegroundQueue = IntGetFocusMessageQueue();
37 return ForegroundQueue != NULL ? ForegroundQueue->FocusWindow : 0;
38 }
39
40 HWND FASTCALL
41 IntGetThreadFocusWindow(VOID)
42 {
43 PTHREADINFO pti;
44 PUSER_MESSAGE_QUEUE ThreadQueue;
45
46 pti = PsGetCurrentThreadWin32Thread();
47 ThreadQueue = pti->MessageQueue;
48 return ThreadQueue != NULL ? ThreadQueue->FocusWindow : 0;
49 }
50
51 VOID FASTCALL
52 co_IntSendDeactivateMessages(HWND hWndPrev, HWND hWnd)
53 {
54 PWINDOW_OBJECT WndPrev ;
55
56 if (hWndPrev && (WndPrev = UserGetWindowObject(hWndPrev)))
57 {
58 co_IntSendMessageNoWait(hWndPrev, WM_NCACTIVATE, FALSE, 0);
59 co_IntSendMessageNoWait(hWndPrev, WM_ACTIVATE,
60 MAKEWPARAM(WA_INACTIVE, WndPrev->Wnd->style & WS_MINIMIZE),
61 (LPARAM)hWnd);
62 }
63 }
64
65 VOID FASTCALL
66 co_IntSendActivateMessages(HWND hWndPrev, HWND hWnd, BOOL MouseActivate)
67 {
68 USER_REFERENCE_ENTRY Ref, RefPrev;
69 PWINDOW_OBJECT Window, WindowPrev = NULL;
70
71 if ((Window = UserGetWindowObject(hWnd)))
72 {
73 UserRefObjectCo(Window, &Ref);
74
75 WindowPrev = UserGetWindowObject(hWndPrev);
76
77 if (WindowPrev) UserRefObjectCo(WindowPrev, &RefPrev);
78
79 /* Send palette messages */
80 if (co_IntPostOrSendMessage(hWnd, WM_QUERYNEWPALETTE, 0, 0))
81 {
82 UserPostMessage( HWND_BROADCAST,
83 WM_PALETTEISCHANGING,
84 (WPARAM)hWnd,
85 0);
86 }
87
88 if (Window->spwndPrev != NULL)
89 co_WinPosSetWindowPos(Window, HWND_TOP, 0, 0, 0, 0,
90 SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOSENDCHANGING);
91
92 if (!Window->spwndOwner && !IntGetParent(Window))
93 {
94 co_IntShellHookNotify(HSHELL_WINDOWACTIVATED, (LPARAM) hWnd);
95 }
96
97 if (Window->Wnd)
98 { // Set last active for window and it's owner.
99 Window->Wnd->hWndLastActive = hWnd;
100 if (Window->Wnd->spwndOwner)
101 Window->Wnd->spwndOwner->hWndLastActive = hWnd;
102 Window->Wnd->state |= WNDS_ACTIVEFRAME;
103 }
104
105 if (WindowPrev && WindowPrev->Wnd)
106 WindowPrev->Wnd->state &= ~WNDS_ACTIVEFRAME;
107
108 if (Window && WindowPrev)
109 {
110 PWINDOW_OBJECT cWindow;
111 HWND *List, *phWnd;
112 HANDLE OldTID = IntGetWndThreadId(WindowPrev);
113 HANDLE NewTID = IntGetWndThreadId(Window);
114
115 DPRINT("SendActiveMessage Old -> %x, New -> %x\n", OldTID, NewTID);
116 if (Window->Wnd->style & WS_MINIMIZE)
117 {
118 DPRINT("Widow was minimized\n");
119 }
120
121 if (OldTID != NewTID)
122 {
123 List = IntWinListChildren(UserGetWindowObject(IntGetDesktopWindow()));
124 if (List)
125 {
126 for (phWnd = List; *phWnd; ++phWnd)
127 {
128 cWindow = UserGetWindowObject(*phWnd);
129
130 if (cWindow && (IntGetWndThreadId(cWindow) == OldTID))
131 { // FALSE if the window is being deactivated,
132 // ThreadId that owns the window being activated.
133 co_IntSendMessageNoWait(*phWnd, WM_ACTIVATEAPP, FALSE, (LPARAM)NewTID);
134 }
135 }
136 for (phWnd = List; *phWnd; ++phWnd)
137 {
138 cWindow = UserGetWindowObject(*phWnd);
139 if (cWindow && (IntGetWndThreadId(cWindow) == NewTID))
140 { // TRUE if the window is being activated,
141 // ThreadId that owns the window being deactivated.
142 co_IntSendMessageNoWait(*phWnd, WM_ACTIVATEAPP, TRUE, (LPARAM)OldTID);
143 }
144 }
145 ExFreePool(List);
146 }
147 }
148 UserDerefObjectCo(WindowPrev); // Now allow the previous window to die.
149 }
150
151 UserDerefObjectCo(Window);
152
153 /* FIXME: IntIsWindow */
154 co_IntSendMessageNoWait(hWnd, WM_NCACTIVATE, (WPARAM)(hWnd == UserGetForegroundWindow()), 0);
155 /* FIXME: WA_CLICKACTIVE */
156 co_IntSendMessageNoWait(hWnd, WM_ACTIVATE,
157 MAKEWPARAM(MouseActivate ? WA_CLICKACTIVE : WA_ACTIVE,
158 Window->Wnd->style & WS_MINIMIZE),
159 (LPARAM)hWndPrev);
160 }
161 }
162
163 VOID FASTCALL
164 co_IntSendKillFocusMessages(HWND hWndPrev, HWND hWnd)
165 {
166 if (hWndPrev)
167 {
168 co_IntPostOrSendMessage(hWndPrev, WM_KILLFOCUS, (WPARAM)hWnd, 0);
169 }
170 }
171
172 VOID FASTCALL
173 co_IntSendSetFocusMessages(HWND hWndPrev, HWND hWnd)
174 {
175 if (hWnd)
176 {
177 co_IntPostOrSendMessage(hWnd, WM_SETFOCUS, (WPARAM)hWndPrev, 0);
178 }
179 }
180
181 HWND FASTCALL
182 IntFindChildWindowToOwner(PWINDOW_OBJECT Root, PWINDOW_OBJECT Owner)
183 {
184 HWND Ret;
185 PWINDOW_OBJECT Child, OwnerWnd;
186
187 for(Child = Root->spwndChild; Child; Child = Child->spwndNext)
188 {
189 OwnerWnd = Child->spwndOwner;
190 if(!OwnerWnd)
191 continue;
192
193 if(OwnerWnd == Owner)
194 {
195 Ret = Child->hSelf;
196 return Ret;
197 }
198 }
199
200 return NULL;
201 }
202
203 static BOOL FASTCALL
204 co_IntSetForegroundAndFocusWindow(PWINDOW_OBJECT Window, PWINDOW_OBJECT FocusWindow, BOOL MouseActivate)
205 {
206 HWND hWnd = Window->hSelf;
207 HWND hWndPrev = NULL;
208 HWND hWndFocus = FocusWindow->hSelf;
209 HWND hWndFocusPrev = NULL;
210 PUSER_MESSAGE_QUEUE PrevForegroundQueue;
211 PWND Wnd;
212
213 ASSERT_REFS_CO(Window);
214
215 DPRINT("IntSetForegroundAndFocusWindow(%x, %x, %s)\n", hWnd, hWndFocus, MouseActivate ? "TRUE" : "FALSE");
216
217 Wnd = Window->Wnd;
218
219 if ((Wnd->style & (WS_CHILD | WS_POPUP)) == WS_CHILD)
220 {
221 DPRINT("Failed - Child\n");
222 return FALSE;
223 }
224
225 if (0 == (Wnd->style & WS_VISIBLE) &&
226 Window->pti->pEThread->ThreadsProcess != CsrProcess)
227 {
228 DPRINT("Failed - Invisible\n");
229 return FALSE;
230 }
231
232 PrevForegroundQueue = IntGetFocusMessageQueue();
233 if (PrevForegroundQueue != 0)
234 {
235 hWndPrev = PrevForegroundQueue->ActiveWindow;
236 hWndFocusPrev = PrevForegroundQueue->FocusWindow;
237 }
238
239 if (hWndPrev == hWnd)
240 {
241 DPRINT("Failed - Same\n");
242 return TRUE;
243 }
244
245 /* FIXME: Call hooks. */
246
247 co_IntSendDeactivateMessages(hWndPrev, hWnd);
248 co_IntSendKillFocusMessages(hWndFocusPrev, hWndFocus);
249
250
251 IntSetFocusMessageQueue(Window->pti->MessageQueue);
252
253 if (Window->pti->MessageQueue)
254 {
255 Window->pti->MessageQueue->ActiveWindow = hWnd;
256 }
257
258 if (FocusWindow->pti->MessageQueue)
259 {
260 FocusWindow->pti->MessageQueue->FocusWindow = hWndFocus;
261 }
262
263 if (PrevForegroundQueue != Window->pti->MessageQueue)
264 {
265 /* FIXME: Send WM_ACTIVATEAPP to all thread windows. */
266 }
267
268 co_IntSendSetFocusMessages(hWndFocusPrev, hWndFocus);
269 co_IntSendActivateMessages(hWndPrev, hWnd, MouseActivate);
270
271 return TRUE;
272 }
273
274 BOOL FASTCALL
275 co_IntSetForegroundWindow(PWINDOW_OBJECT Window)//FIXME: can Window be NULL??
276 {
277 /*if (Window)*/ ASSERT_REFS_CO(Window);
278
279 return co_IntSetForegroundAndFocusWindow(Window, Window, FALSE);
280 }
281
282 BOOL FASTCALL
283 co_IntMouseActivateWindow(PWINDOW_OBJECT Window)
284 {
285 HWND Top;
286 PWINDOW_OBJECT TopWindow;
287 USER_REFERENCE_ENTRY Ref;
288 PWND Wnd;
289
290 ASSERT_REFS_CO(Window);
291
292 Wnd = Window->Wnd;
293 if(Wnd->style & WS_DISABLED)
294 {
295 BOOL Ret;
296 PWINDOW_OBJECT TopWnd;
297 PWINDOW_OBJECT DesktopWindow = UserGetWindowObject(IntGetDesktopWindow());
298 if(DesktopWindow)
299 {
300 Top = IntFindChildWindowToOwner(DesktopWindow, Window);
301 if((TopWnd = UserGetWindowObject(Top)))
302 {
303 UserRefObjectCo(TopWnd, &Ref);
304 Ret = co_IntMouseActivateWindow(TopWnd);
305 UserDerefObjectCo(TopWnd);
306
307 return Ret;
308 }
309 }
310 return FALSE;
311 }
312
313
314 TopWindow = UserGetAncestor(Window, GA_ROOT);
315 if (!TopWindow) return FALSE;
316
317 /* TMN: Check return valud from this function? */
318 UserRefObjectCo(TopWindow, &Ref);
319
320 co_IntSetForegroundAndFocusWindow(TopWindow, Window, TRUE);
321
322 UserDerefObjectCo(TopWindow);
323
324 return TRUE;
325 }
326
327 HWND FASTCALL
328 co_IntSetActiveWindow(PWINDOW_OBJECT Window OPTIONAL)
329 {
330 PTHREADINFO pti;
331 PUSER_MESSAGE_QUEUE ThreadQueue;
332 HWND hWndPrev;
333 HWND hWnd = 0;
334 PWND Wnd;
335 CBTACTIVATESTRUCT cbt;
336
337 if (Window)
338 ASSERT_REFS_CO(Window);
339
340 pti = PsGetCurrentThreadWin32Thread();
341 ThreadQueue = pti->MessageQueue;
342 ASSERT(ThreadQueue != 0);
343
344 if (Window != 0)
345 {
346 Wnd = Window->Wnd;
347 if ((!(Wnd->style & WS_VISIBLE) &&
348 Window->pti->pEThread->ThreadsProcess != CsrProcess) ||
349 (Wnd->style & (WS_POPUP | WS_CHILD)) == WS_CHILD)
350 {
351 return ThreadQueue ? 0 : ThreadQueue->ActiveWindow;
352 }
353 hWnd = Window->hSelf;
354 }
355
356 hWndPrev = ThreadQueue->ActiveWindow;
357 if (hWndPrev == hWnd)
358 {
359 return hWndPrev;
360 }
361
362 /* call CBT hook chain */
363 cbt.fMouse = FALSE;
364 cbt.hWndActive = hWndPrev;
365 if (co_HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hWnd, (LPARAM)&cbt))
366 return 0;
367
368 ThreadQueue->ActiveWindow = hWnd;
369
370 co_IntSendDeactivateMessages(hWndPrev, hWnd);
371 co_IntSendActivateMessages(hWndPrev, hWnd, FALSE);
372
373 /* FIXME */
374 /* return IntIsWindow(hWndPrev) ? hWndPrev : 0;*/
375 return hWndPrev;
376 }
377
378 static
379 HWND FASTCALL
380 co_IntSetFocusWindow(PWINDOW_OBJECT Window OPTIONAL)
381 {
382 HWND hWndPrev = 0;
383 PTHREADINFO pti;
384 PUSER_MESSAGE_QUEUE ThreadQueue;
385
386 if (Window)
387 ASSERT_REFS_CO(Window);
388
389 pti = PsGetCurrentThreadWin32Thread();
390 ThreadQueue = pti->MessageQueue;
391 ASSERT(ThreadQueue != 0);
392
393 hWndPrev = ThreadQueue->FocusWindow;
394
395 if (Window != 0)
396 {
397 if (hWndPrev == Window->hSelf)
398 {
399 return hWndPrev;
400 }
401
402 if (co_HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)Window->hSelf, (LPARAM)hWndPrev))
403 return 0;
404
405 ThreadQueue->FocusWindow = Window->hSelf;
406
407 co_IntSendKillFocusMessages(hWndPrev, Window->hSelf);
408 co_IntSendSetFocusMessages(hWndPrev, Window->hSelf);
409 }
410 else
411 {
412 ThreadQueue->FocusWindow = 0;
413
414 if (co_HOOK_CallHooks( WH_CBT, HCBT_SETFOCUS, (WPARAM)0, (LPARAM)hWndPrev))
415 return 0;
416
417 co_IntSendKillFocusMessages(hWndPrev, 0);
418 }
419 return hWndPrev;
420 }
421
422
423 /*
424 * @implemented
425 */
426 HWND FASTCALL
427 UserGetForegroundWindow(VOID)
428 {
429 PUSER_MESSAGE_QUEUE ForegroundQueue;
430
431 ForegroundQueue = IntGetFocusMessageQueue();
432 return( ForegroundQueue != NULL ? ForegroundQueue->ActiveWindow : 0);
433 }
434
435
436 /*
437 * @implemented
438 */
439 HWND APIENTRY
440 NtUserGetForegroundWindow(VOID)
441 {
442 DECLARE_RETURN(HWND);
443
444 DPRINT("Enter NtUserGetForegroundWindow\n");
445 UserEnterExclusive();
446
447 RETURN( UserGetForegroundWindow());
448
449 CLEANUP:
450 DPRINT("Leave NtUserGetForegroundWindow, ret=%i\n",_ret_);
451 UserLeave();
452 END_CLEANUP;
453 }
454
455
456 HWND FASTCALL UserGetActiveWindow(VOID)
457 {
458 PTHREADINFO pti;
459 PUSER_MESSAGE_QUEUE ThreadQueue;
460
461 pti = PsGetCurrentThreadWin32Thread();
462 ThreadQueue = pti->MessageQueue;
463 return( ThreadQueue ? ThreadQueue->ActiveWindow : 0);
464 }
465
466
467 HWND APIENTRY
468 NtUserSetActiveWindow(HWND hWnd)
469 {
470 USER_REFERENCE_ENTRY Ref;
471 DECLARE_RETURN(HWND);
472
473 DPRINT("Enter NtUserSetActiveWindow(%x)\n", hWnd);
474 UserEnterExclusive();
475
476 if (hWnd)
477 {
478 PWINDOW_OBJECT Window;
479 PTHREADINFO pti;
480 PUSER_MESSAGE_QUEUE ThreadQueue;
481 HWND hWndPrev;
482
483 if (!(Window = UserGetWindowObject(hWnd)))
484 {
485 RETURN( 0);
486 }
487
488 pti = PsGetCurrentThreadWin32Thread();
489 ThreadQueue = pti->MessageQueue;
490
491 if (Window->pti->MessageQueue != ThreadQueue)
492 {
493 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
494 RETURN( 0);
495 }
496
497 UserRefObjectCo(Window, &Ref);
498 hWndPrev = co_IntSetActiveWindow(Window);
499 UserDerefObjectCo(Window);
500
501 RETURN( hWndPrev);
502 }
503 else
504 {
505 RETURN( co_IntSetActiveWindow(0));
506 }
507
508 CLEANUP:
509 DPRINT("Leave NtUserSetActiveWindow, ret=%i\n",_ret_);
510 UserLeave();
511 END_CLEANUP;
512 }
513
514 /*
515 * @implemented
516 */
517 HWND APIENTRY
518 IntGetCapture(VOID)
519 {
520 PTHREADINFO pti;
521 PUSER_MESSAGE_QUEUE ThreadQueue;
522 DECLARE_RETURN(HWND);
523
524 DPRINT("Enter IntGetCapture\n");
525
526 pti = PsGetCurrentThreadWin32Thread();
527 ThreadQueue = pti->MessageQueue;
528 RETURN( ThreadQueue ? ThreadQueue->CaptureWindow : 0);
529
530 CLEANUP:
531 DPRINT("Leave IntGetCapture, ret=%i\n",_ret_);
532 END_CLEANUP;
533 }
534
535 /*
536 * @implemented
537 */
538 HWND APIENTRY
539 NtUserSetCapture(HWND hWnd)
540 {
541 PTHREADINFO pti;
542 PUSER_MESSAGE_QUEUE ThreadQueue;
543 PWINDOW_OBJECT Window;
544 HWND hWndPrev;
545 DECLARE_RETURN(HWND);
546
547 DPRINT("Enter NtUserSetCapture(%x)\n", hWnd);
548 UserEnterExclusive();
549
550 pti = PsGetCurrentThreadWin32Thread();
551 ThreadQueue = pti->MessageQueue;
552
553 if((Window = UserGetWindowObject(hWnd)))
554 {
555 if(Window->pti->MessageQueue != ThreadQueue)
556 {
557 RETURN(NULL);
558 }
559 }
560
561 hWndPrev = MsqSetStateWindow(ThreadQueue, MSQ_STATE_CAPTURE, hWnd);
562
563 /* also remove other windows if not capturing anymore */
564 if(hWnd == NULL)
565 {
566 MsqSetStateWindow(ThreadQueue, MSQ_STATE_MENUOWNER, NULL);
567 MsqSetStateWindow(ThreadQueue, MSQ_STATE_MOVESIZE, NULL);
568 }
569
570 co_IntPostOrSendMessage(hWndPrev, WM_CAPTURECHANGED, 0, (LPARAM)hWnd);
571 ThreadQueue->CaptureWindow = hWnd;
572
573 RETURN( hWndPrev);
574
575 CLEANUP:
576 DPRINT("Leave NtUserSetCapture, ret=%i\n",_ret_);
577 UserLeave();
578 END_CLEANUP;
579 }
580
581
582
583 HWND FASTCALL co_UserSetFocus(PWINDOW_OBJECT Window OPTIONAL)
584 {
585 if (Window)
586 {
587 PTHREADINFO pti;
588 PUSER_MESSAGE_QUEUE ThreadQueue;
589 HWND hWndPrev;
590 PWINDOW_OBJECT TopWnd;
591 USER_REFERENCE_ENTRY Ref;
592 PWND Wnd;
593
594 ASSERT_REFS_CO(Window);
595
596 pti = PsGetCurrentThreadWin32Thread();
597 ThreadQueue = pti->MessageQueue;
598
599 Wnd = Window->Wnd;
600 if (Wnd->style & (WS_MINIMIZE | WS_DISABLED))
601 {
602 return( (ThreadQueue ? ThreadQueue->FocusWindow : 0));
603 }
604
605 if (Window->pti->MessageQueue != ThreadQueue)
606 {
607 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
608 return( 0);
609 }
610
611 TopWnd = UserGetAncestor(Window, GA_ROOT);
612 if (TopWnd && TopWnd->hSelf != UserGetActiveWindow())
613 {
614 // PWINDOW_OBJECT WndTops = UserGetWindowObject(hWndTop);
615 UserRefObjectCo(TopWnd, &Ref);
616 co_IntSetActiveWindow(TopWnd);
617 UserDerefObjectCo(TopWnd);
618 }
619
620 hWndPrev = co_IntSetFocusWindow(Window);
621
622 return( hWndPrev);
623 }
624 else
625 {
626 return( co_IntSetFocusWindow(NULL));
627 }
628
629 }
630
631
632 /*
633 * @implemented
634 */
635 HWND APIENTRY
636 NtUserSetFocus(HWND hWnd)
637 {
638 PWINDOW_OBJECT Window;
639 USER_REFERENCE_ENTRY Ref;
640 DECLARE_RETURN(HWND);
641 HWND ret;
642
643 DPRINT("Enter NtUserSetFocus(%x)\n", hWnd);
644 UserEnterExclusive();
645
646 if (hWnd)
647 {
648 if (!(Window = UserGetWindowObject(hWnd)))
649 {
650 RETURN(NULL);
651 }
652
653 UserRefObjectCo(Window, &Ref);
654 ret = co_UserSetFocus(Window);
655 UserDerefObjectCo(Window);
656
657 RETURN(ret);
658 }
659 else
660 {
661 RETURN( co_UserSetFocus(0));
662 }
663
664 CLEANUP:
665 DPRINT("Leave NtUserSetFocus, ret=%i\n",_ret_);
666 UserLeave();
667 END_CLEANUP;
668 }
669
670 /* EOF */