[Win32SS]
[reactos.git] / reactos / win32ss / user / ntuser / painting.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS Win32k subsystem
4 * PURPOSE: Window painting function
5 * FILE: win32ss/user/ntuser/painting.c
6 * PROGRAMER: Filip Navara (xnavara@volny.cz)
7 */
8
9 #include <win32k.h>
10 DBG_DEFAULT_CHANNEL(UserPainting);
11
12 /* PRIVATE FUNCTIONS **********************************************************/
13
14 /**
15 * @name IntIntersectWithParents
16 *
17 * Intersect window rectangle with all parent client rectangles.
18 *
19 * @param Child
20 * Pointer to child window to start intersecting from.
21 * @param WindowRect
22 * Pointer to rectangle that we want to intersect in screen
23 * coordinates on input and intersected rectangle on output (if TRUE
24 * is returned).
25 *
26 * @return
27 * If any parent is minimized or invisible or the resulting rectangle
28 * is empty then FALSE is returned. Otherwise TRUE is returned.
29 */
30
31 BOOL FASTCALL
32 IntIntersectWithParents(PWND Child, RECTL *WindowRect)
33 {
34 PWND ParentWnd;
35
36 if (Child->ExStyle & WS_EX_REDIRECTED)
37 return TRUE;
38
39 ParentWnd = Child->spwndParent;
40 while (ParentWnd != NULL)
41 {
42 if (!(ParentWnd->style & WS_VISIBLE) ||
43 (ParentWnd->style & WS_MINIMIZE) ||
44 !RECTL_bIntersectRect(WindowRect, WindowRect, &ParentWnd->rcClient) )
45 {
46 return FALSE;
47 }
48
49 if (ParentWnd->ExStyle & WS_EX_REDIRECTED)
50 return TRUE;
51
52 ParentWnd = ParentWnd->spwndParent;
53 }
54
55 return TRUE;
56 }
57
58 BOOL FASTCALL
59 IntValidateParents(PWND Child, BOOL Recurse)
60 {
61 RECTL ParentRect, Rect;
62 BOOL Start, Ret = TRUE;
63 PWND ParentWnd = Child;
64 PREGION Rgn = NULL;
65
66 if (ParentWnd->style & WS_CHILD)
67 {
68 do
69 ParentWnd = ParentWnd->spwndParent;
70 while (ParentWnd->style & WS_CHILD);
71 }
72
73 // No pending nonclient paints.
74 if (!(ParentWnd->state & WNDS_SYNCPAINTPENDING)) Recurse = FALSE;
75
76 Start = TRUE;
77 ParentWnd = Child->spwndParent;
78 while (ParentWnd)
79 {
80 if (ParentWnd->style & WS_CLIPCHILDREN)
81 break;
82
83 if (ParentWnd->hrgnUpdate != 0)
84 {
85 if (Recurse)
86 {
87 Ret = FALSE;
88 break;
89 }
90 // Start with child clipping.
91 if (Start)
92 {
93 Start = FALSE;
94
95 Rect = Child->rcWindow;
96
97 if (!IntIntersectWithParents(Child, &Rect)) break;
98
99 Rgn = IntSysCreateRectpRgnIndirect(&Rect);
100
101 if (Child->hrgnClip)
102 {
103 PREGION RgnClip = REGION_LockRgn(Child->hrgnClip);
104 IntGdiCombineRgn(Rgn, Rgn, RgnClip, RGN_AND);
105 REGION_UnlockRgn(RgnClip);
106 }
107 }
108
109 ParentRect = ParentWnd->rcWindow;
110
111 if (!IntIntersectWithParents(ParentWnd, &ParentRect)) break;
112
113 IntInvalidateWindows( ParentWnd,
114 Rgn,
115 RDW_VALIDATE | RDW_NOCHILDREN | RDW_NOUPDATEDIRTY);
116 }
117 ParentWnd = ParentWnd->spwndParent;
118 }
119
120 if (Rgn) REGION_Delete(Rgn);
121
122 return Ret;
123 }
124
125 /*
126 Synchronize painting to the top-level windows of other threads.
127 */
128 VOID FASTCALL
129 IntSendSyncPaint(PWND Wnd, ULONG Flags)
130 {
131 PTHREADINFO ptiCur, ptiWnd;
132 PUSER_SENT_MESSAGE Message;
133 PLIST_ENTRY Entry;
134 BOOL bSend = TRUE;
135
136 ptiWnd = Wnd->head.pti;
137 ptiCur = PsGetCurrentThreadWin32Thread();
138 /*
139 Not the current thread, Wnd is in send Nonclient paint also in send erase background and it is visiable.
140 */
141 if ( Wnd->head.pti != ptiCur &&
142 Wnd->state & WNDS_SENDNCPAINT &&
143 Wnd->state & WNDS_SENDERASEBACKGROUND &&
144 Wnd->style & WS_VISIBLE)
145 {
146 // For testing, if you see this, break out the Champagne and have a party!
147 TRACE("SendSyncPaint Wnd in State!\n");
148 if (!IsListEmpty(&ptiWnd->SentMessagesListHead))
149 {
150 // Scan sent queue messages to see if we received sync paint messages.
151 Entry = ptiWnd->SentMessagesListHead.Flink;
152 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
153 do
154 {
155 ERR("LOOP it\n");
156 if (Message->Msg.message == WM_SYNCPAINT &&
157 Message->Msg.hwnd == UserHMGetHandle(Wnd))
158 { // Already received so exit out.
159 ERR("SendSyncPaint Found one in the Sent Msg Queue!\n");
160 bSend = FALSE;
161 break;
162 }
163 Entry = Message->ListEntry.Flink;
164 Message = CONTAINING_RECORD(Entry, USER_SENT_MESSAGE, ListEntry);
165 }
166 while (Entry != &ptiWnd->SentMessagesListHead);
167 }
168 if (bSend)
169 {
170 TRACE("Sending WM_SYNCPAINT\n");
171 // This message has no parameters. But it does! Pass Flags along.
172 co_IntSendMessageNoWait(UserHMGetHandle(Wnd), WM_SYNCPAINT, Flags, 0);
173 Wnd->state |= WNDS_SYNCPAINTPENDING;
174 }
175 }
176
177 // Send to all the children if this is the desktop window.
178 if ( Wnd == UserGetDesktopWindow() )
179 {
180 if ( Flags & RDW_ALLCHILDREN ||
181 ( !(Flags & RDW_NOCHILDREN) && Wnd->style & WS_CLIPCHILDREN))
182 {
183 PWND spwndChild = Wnd->spwndChild;
184 while(spwndChild)
185 {
186 if ( spwndChild->style & WS_CHILD &&
187 spwndChild->head.pti != ptiCur)
188 {
189 spwndChild = spwndChild->spwndNext;
190 continue;
191 }
192 IntSendSyncPaint( spwndChild, Flags );
193 spwndChild = spwndChild->spwndNext;
194 }
195 }
196 }
197 }
198
199 /*
200 * @name IntCalcWindowRgn
201 *
202 * Get a window or client region.
203 */
204
205 HRGN FASTCALL
206 IntCalcWindowRgn(PWND Wnd, BOOL Client)
207 {
208 HRGN hRgnWindow;
209
210 if (Client)
211 {
212 hRgnWindow = NtGdiCreateRectRgn(
213 Wnd->rcClient.left,
214 Wnd->rcClient.top,
215 Wnd->rcClient.right,
216 Wnd->rcClient.bottom);
217 }
218 else
219 {
220 hRgnWindow = NtGdiCreateRectRgn(
221 Wnd->rcWindow.left,
222 Wnd->rcWindow.top,
223 Wnd->rcWindow.right,
224 Wnd->rcWindow.bottom);
225 }
226
227 if (Wnd->hrgnClip != NULL && !(Wnd->style & WS_MINIMIZE))
228 {
229 NtGdiOffsetRgn(hRgnWindow,
230 -Wnd->rcWindow.left,
231 -Wnd->rcWindow.top);
232 NtGdiCombineRgn(hRgnWindow, hRgnWindow, Wnd->hrgnClip, RGN_AND);
233 NtGdiOffsetRgn(hRgnWindow,
234 Wnd->rcWindow.left,
235 Wnd->rcWindow.top);
236 }
237
238 return hRgnWindow;
239 }
240
241 /*
242 * @name IntGetNCUpdateRgn
243 *
244 * Get non-client update region of a window and optionally validate it.
245 *
246 * @param Window
247 * Pointer to window to get the NC update region from.
248 * @param Validate
249 * Set to TRUE to force validating the NC update region.
250 *
251 * @return
252 * Handle to NC update region. The caller is responsible for deleting
253 * it.
254 */
255
256 HRGN FASTCALL
257 IntGetNCUpdateRgn(PWND Window, BOOL Validate)
258 {
259 HRGN hRgnNonClient;
260 HRGN hRgnWindow;
261 UINT RgnType, NcType;
262 RECT update;
263
264 if (Window->hrgnUpdate != NULL &&
265 Window->hrgnUpdate != HRGN_WINDOW)
266 {
267 hRgnNonClient = IntCalcWindowRgn(Window, FALSE);
268
269 /*
270 * If region creation fails it's safe to fallback to whole
271 * window region.
272 */
273 if (hRgnNonClient == NULL)
274 {
275 return HRGN_WINDOW;
276 }
277
278 hRgnWindow = IntCalcWindowRgn(Window, TRUE);
279 if (hRgnWindow == NULL)
280 {
281 GreDeleteObject(hRgnNonClient);
282 return HRGN_WINDOW;
283 }
284
285 NcType = IntGdiGetRgnBox(hRgnNonClient, &update);
286
287 RgnType = NtGdiCombineRgn(hRgnNonClient, hRgnNonClient, hRgnWindow, RGN_DIFF);
288
289 if (RgnType == ERROR)
290 {
291 GreDeleteObject(hRgnWindow);
292 GreDeleteObject(hRgnNonClient);
293 return HRGN_WINDOW;
294 }
295 else if (RgnType == NULLREGION)
296 {
297 GreDeleteObject(hRgnWindow);
298 GreDeleteObject(hRgnNonClient);
299 Window->state &= ~WNDS_UPDATEDIRTY;
300 return NULL;
301 }
302
303 /*
304 * Remove the nonclient region from the standard update region if
305 * we were asked for it.
306 */
307
308 if (Validate)
309 {
310 if (NtGdiCombineRgn(Window->hrgnUpdate, Window->hrgnUpdate, hRgnWindow, RGN_AND) == NULLREGION)
311 {
312 IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
313 GreDeleteObject(Window->hrgnUpdate);
314 Window->state &= ~WNDS_UPDATEDIRTY;
315 Window->hrgnUpdate = NULL;
316 if (!(Window->state & WNDS_INTERNALPAINT))
317 MsqDecPaintCountQueue(Window->head.pti);
318 }
319 }
320
321 /* check if update rgn contains complete nonclient area */
322 if (NcType == SIMPLEREGION)
323 {
324 RECT window;
325 IntGetWindowRect( Window, &window );
326
327 if (IntEqualRect( &window, &update ))
328 {
329 GreDeleteObject(hRgnNonClient);
330 hRgnNonClient = HRGN_WINDOW;
331 }
332 }
333
334 GreDeleteObject(hRgnWindow);
335
336 return hRgnNonClient;
337 }
338 else
339 {
340 return Window->hrgnUpdate;
341 }
342 }
343
344 VOID FASTCALL
345 IntSendNCPaint(PWND pWnd, HRGN hRgn)
346 {
347 pWnd->state &= ~WNDS_SENDNCPAINT;
348
349 if ( pWnd == GetW32ThreadInfo()->MessageQueue->spwndActive &&
350 !(pWnd->state & WNDS_ACTIVEFRAME))
351 {
352 pWnd->state |= WNDS_ACTIVEFRAME;
353 pWnd->state &= ~WNDS_NONCPAINT;
354 hRgn = HRGN_WINDOW;
355 }
356
357 if (pWnd->state2 & WNDS2_FORCEFULLNCPAINTCLIPRGN)
358 {
359 pWnd->state2 &= ~WNDS2_FORCEFULLNCPAINTCLIPRGN;
360 hRgn = HRGN_WINDOW;
361 }
362
363 if (hRgn) co_IntSendMessage(UserHMGetHandle(pWnd), WM_NCPAINT, (WPARAM)hRgn, 0);
364 }
365
366 VOID FASTCALL
367 IntSendChildNCPaint(PWND pWnd)
368 {
369 PWND Child;
370 HWND *List, *phWnd;
371
372 List = IntWinListChildren(UserGetDesktopWindow());
373 if ( List )
374 {
375 for (phWnd = List; *phWnd; ++phWnd)
376 {
377 Child = ValidateHwndNoErr(*phWnd);
378 if ( Child && Child->hrgnUpdate == NULL && Child->state & WNDS_SENDNCPAINT)
379 {
380 USER_REFERENCE_ENTRY Ref;
381 UserRefObjectCo(Child, &Ref);
382 IntSendNCPaint(Child, HRGN_WINDOW);
383 UserDerefObjectCo(Child);
384 }
385 }
386 ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
387 }
388 /* FIXME : Use snap shot mode until window death is fixed while surfing menus! Fix CORE-12085 and CORE-12071.
389 pWnd = pWnd->spwndChild;
390 while(pWnd)
391 {
392 if (pWnd->hrgnUpdate == NULL && pWnd->state & WNDS_SENDNCPAINT)
393 {
394 USER_REFERENCE_ENTRY Ref;
395 UserRefObjectCo(pWnd, &Ref);
396 IntSendNCPaint(pWnd, HRGN_WINDOW);
397 UserDerefObjectCo(pWnd);
398 }
399 pWnd = pWnd->spwndNext;
400 }*/
401 }
402
403 /*
404 * IntPaintWindows
405 *
406 * Internal function used by IntRedrawWindow.
407 */
408
409 VOID FASTCALL
410 co_IntPaintWindows(PWND Wnd, ULONG Flags, BOOL Recurse)
411 {
412 HDC hDC;
413 HWND hWnd = Wnd->head.h;
414 HRGN TempRegion = NULL;
415
416 Wnd->state &= ~WNDS_PAINTNOTPROCESSED;
417
418 if (Wnd->state & WNDS_SENDNCPAINT ||
419 Wnd->state & WNDS_SENDERASEBACKGROUND)
420 {
421 if (!(Wnd->style & WS_VISIBLE))
422 {
423 Wnd->state &= ~(WNDS_SENDNCPAINT|WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
424 return;
425 }
426 else
427 {
428 if (Wnd->hrgnUpdate == NULL)
429 {
430 Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
431 }
432
433 if (Wnd->head.pti == PsGetCurrentThreadWin32Thread())
434 {
435 if (Wnd->state & WNDS_SENDNCPAINT)
436 {
437 TempRegion = IntGetNCUpdateRgn(Wnd, TRUE);
438
439 IntSendNCPaint(Wnd, TempRegion);
440
441 if (TempRegion > HRGN_WINDOW && GreIsHandleValid(TempRegion))
442 {
443 /* NOTE: The region can already be deleted! */
444 GreDeleteObject(TempRegion);
445 }
446 }
447
448 if (Wnd->state & WNDS_SENDERASEBACKGROUND)
449 {
450 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
451 if (Wnd->hrgnUpdate)
452 {
453 hDC = UserGetDCEx( Wnd,
454 Wnd->hrgnUpdate,
455 DCX_CACHE|DCX_USESTYLE|DCX_INTERSECTRGN|DCX_KEEPCLIPRGN);
456
457 if (Wnd->head.pti->ppi != pti->ppi)
458 {
459 ERR("Sending DC to another Process!!!\n");
460 }
461
462 Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
463 // Kill the loop, so Clear before we send.
464 if (!co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
465 {
466 Wnd->state |= (WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
467 }
468 UserReleaseDC(Wnd, hDC, FALSE);
469 }
470 }
471 }
472
473 }
474 }
475
476 /*
477 * Check that the window is still valid at this point
478 */
479 if (!IntIsWindow(hWnd))
480 {
481 return;
482 }
483
484 /*
485 * Paint child windows.
486 */
487
488 if (!(Flags & RDW_NOCHILDREN) &&
489 !(Wnd->style & WS_MINIMIZE) &&
490 ( Flags & RDW_ALLCHILDREN ||
491 (Flags & RDW_CLIPCHILDREN && Wnd->style & WS_CLIPCHILDREN) ) )
492 {
493 HWND *List, *phWnd;
494 PTHREADINFO pti = PsGetCurrentThreadWin32Thread();
495
496 if ((List = IntWinListChildren(Wnd)))
497 {
498 for (phWnd = List; *phWnd; ++phWnd)
499 {
500 if ((Wnd = UserGetWindowObject(*phWnd)) == NULL)
501 continue;
502
503 if (Wnd->head.pti != pti && Wnd->style & WS_CHILD)
504 continue;
505
506 if (Wnd->style & WS_VISIBLE)
507 {
508 USER_REFERENCE_ENTRY Ref;
509 UserRefObjectCo(Wnd, &Ref);
510 co_IntPaintWindows(Wnd, Flags, TRUE);
511 UserDerefObjectCo(Wnd);
512 }
513 }
514 ExFreePoolWithTag(List, USERTAG_WINDOWLIST);
515 }
516 }
517 }
518
519 /*
520 * IntUpdateWindows
521 *
522 * Internal function used by IntRedrawWindow, simplecall.
523 */
524
525 VOID FASTCALL
526 co_IntUpdateWindows(PWND Wnd, ULONG Flags, BOOL Recurse)
527 {
528 HWND hWnd = Wnd->head.h;
529
530 if ( Wnd->hrgnUpdate != NULL || Wnd->state & WNDS_INTERNALPAINT )
531 {
532 if (Wnd->hrgnUpdate)
533 {
534 if (!IntValidateParents(Wnd, Recurse))
535 {
536 return;
537 }
538 }
539
540 if (Wnd->state & WNDS_INTERNALPAINT)
541 {
542 Wnd->state &= ~WNDS_INTERNALPAINT;
543
544 if (Wnd->hrgnUpdate == NULL)
545 MsqDecPaintCountQueue(Wnd->head.pti);
546 }
547
548 Wnd->state |= WNDS_PAINTNOTPROCESSED;
549 Wnd->state &= ~WNDS_UPDATEDIRTY;
550
551 Wnd->state2 |= WNDS2_WMPAINTSENT;
552 co_IntSendMessage(hWnd, WM_PAINT, 0, 0);
553
554 if (Wnd->state & WNDS_PAINTNOTPROCESSED)
555 {
556 USER_REFERENCE_ENTRY Ref;
557 UserRefObjectCo(Wnd, &Ref);
558 co_IntPaintWindows(Wnd, RDW_NOCHILDREN, FALSE);
559 UserDerefObjectCo(Wnd);
560 }
561 }
562
563 // Force flags as a toggle. Fixes msg:test_paint_messages:WmChildPaintNc.
564 Flags = (Flags & RDW_NOCHILDREN) ? RDW_NOCHILDREN : RDW_ALLCHILDREN; // All children is the default.
565
566 /*
567 * Update child windows.
568 */
569
570 if (!(Flags & RDW_NOCHILDREN) &&
571 Flags & RDW_ALLCHILDREN &&
572 Wnd != UserGetDesktopWindow() )
573 {
574 PWND Child;
575
576 for (Child = Wnd->spwndChild; Child; Child = Child->spwndNext)
577 {
578 /* transparent window, check for non-transparent sibling to paint first, then skip it */
579 if ( Child->ExStyle & WS_EX_TRANSPARENT &&
580 ( Child->hrgnUpdate != NULL || Child->state & WNDS_INTERNALPAINT ) )
581 {
582 PWND Next = Child->spwndNext;
583 while (Next)
584 {
585 if ( Next->hrgnUpdate != NULL || Next->state & WNDS_INTERNALPAINT ) break;
586
587 Next = Next->spwndNext;
588 }
589
590 if (Next) continue;
591 }
592
593 if (Child->style & WS_VISIBLE)
594 {
595 USER_REFERENCE_ENTRY Ref;
596 UserRefObjectCo(Child, &Ref);
597 co_IntUpdateWindows(Child, Flags, TRUE);
598 UserDerefObjectCo(Child);
599 }
600 }
601 }
602 }
603
604 VOID FASTCALL
605 UserUpdateWindows(PWND pWnd, ULONG Flags)
606 {
607 // If transparent and any sibling windows below needs to be painted, leave.
608 if (pWnd->ExStyle & WS_EX_TRANSPARENT)
609 {
610 PWND Next = pWnd->spwndNext;
611
612 while(Next)
613 {
614 if ( Next->head.pti == pWnd->head.pti &&
615 ( Next->hrgnUpdate != NULL || Next->state & WNDS_INTERNALPAINT) )
616 {
617 return;
618 }
619
620 Next = Next->spwndNext;
621 }
622 }
623 co_IntUpdateWindows(pWnd, Flags, FALSE);
624 }
625
626 VOID FASTCALL
627 UserSyncAndPaintWindows(PWND pWnd, ULONG Flags)
628 {
629 PWND Parent = pWnd;
630 // Find parent, if it needs to be painted, leave.
631 while(TRUE)
632 {
633 if ((Parent = Parent->spwndParent) == NULL) break;
634 if ( Parent->style & WS_CLIPCHILDREN ) break;
635 if ( Parent->hrgnUpdate != NULL || Parent->state & WNDS_INTERNALPAINT ) return;
636 }
637
638 IntSendSyncPaint(pWnd, Flags);
639 co_IntPaintWindows(pWnd, Flags, FALSE);
640 }
641
642 /*
643 * IntInvalidateWindows
644 *
645 * Internal function used by IntRedrawWindow, UserRedrawDesktop,
646 * co_WinPosSetWindowPos, co_UserRedrawWindow.
647 */
648 VOID FASTCALL
649 IntInvalidateWindows(PWND Wnd, PREGION Rgn, ULONG Flags)
650 {
651 INT RgnType = NULLREGION;
652 BOOL HadPaintMessage;
653
654 TRACE("IntInvalidateWindows start Rgn %p\n",Rgn);
655
656 if ( Rgn > PRGN_WINDOW )
657 {
658 /*
659 * If the nonclient is not to be redrawn, clip the region to the client
660 * rect
661 */
662 if ((Flags & RDW_INVALIDATE) != 0 && (Flags & RDW_FRAME) == 0)
663 {
664 PREGION RgnClient;
665
666 RgnClient = IntSysCreateRectpRgnIndirect(&Wnd->rcClient);
667 if (RgnClient)
668 {
669 RgnType = IntGdiCombineRgn(Rgn, Rgn, RgnClient, RGN_AND);
670 REGION_Delete(RgnClient);
671 }
672 }
673
674 /*
675 * Clip the given region with window rectangle (or region)
676 */
677
678 if (!Wnd->hrgnClip || (Wnd->style & WS_MINIMIZE))
679 {
680 PREGION RgnWindow = IntSysCreateRectpRgnIndirect(&Wnd->rcWindow);
681 if (RgnWindow)
682 {
683 RgnType = IntGdiCombineRgn(Rgn, Rgn, RgnWindow, RGN_AND);
684 REGION_Delete(RgnWindow);
685 }
686 }
687 else
688 {
689 PREGION RgnClip = REGION_LockRgn(Wnd->hrgnClip);
690 if (RgnClip)
691 {
692 REGION_bOffsetRgn(Rgn,
693 -Wnd->rcWindow.left,
694 -Wnd->rcWindow.top);
695 RgnType = IntGdiCombineRgn(Rgn, Rgn, RgnClip, RGN_AND);
696 REGION_bOffsetRgn(Rgn,
697 Wnd->rcWindow.left,
698 Wnd->rcWindow.top);
699 REGION_UnlockRgn(RgnClip);
700 }
701 }
702 }
703 else
704 {
705 RgnType = NULLREGION;
706 }
707
708 /*
709 * Save current state of pending updates
710 */
711
712 HadPaintMessage = IntIsWindowDirty(Wnd);
713
714 /*
715 * Update the region and flags
716 */
717
718 // The following flags are used to invalidate the window.
719 if (Flags & (RDW_INVALIDATE|RDW_INTERNALPAINT|RDW_ERASE|RDW_FRAME))
720 {
721 if (Flags & RDW_INTERNALPAINT)
722 {
723 Wnd->state |= WNDS_INTERNALPAINT;
724 }
725
726 if (Flags & RDW_INVALIDATE )
727 {
728 PREGION RgnUpdate;
729
730 Wnd->state &= ~WNDS_NONCPAINT;
731
732 /* If not the same thread set it dirty. */
733 if (Wnd->head.pti != PsGetCurrentThreadWin32Thread())
734 {
735 Wnd->state |= WNDS_UPDATEDIRTY;
736 if (Wnd->state2 & WNDS2_WMPAINTSENT)
737 Wnd->state2 |= WNDS2_ENDPAINTINVALIDATE;
738 }
739
740 if (Flags & RDW_FRAME)
741 Wnd->state |= WNDS_SENDNCPAINT;
742
743 if (Flags & RDW_ERASE)
744 Wnd->state |= WNDS_SENDERASEBACKGROUND;
745
746 if (RgnType != NULLREGION && Rgn > PRGN_WINDOW)
747 {
748 if (Wnd->hrgnUpdate == NULL)
749 {
750 Wnd->hrgnUpdate = NtGdiCreateRectRgn(0, 0, 0, 0);
751 IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_PUBLIC);
752 }
753
754 if (Wnd->hrgnUpdate != HRGN_WINDOW)
755 {
756 RgnUpdate = REGION_LockRgn(Wnd->hrgnUpdate);
757 if (RgnUpdate)
758 {
759 RgnType = IntGdiCombineRgn(RgnUpdate, RgnUpdate, Rgn, RGN_OR);
760 REGION_UnlockRgn(RgnUpdate);
761 if (RgnType == NULLREGION)
762 {
763 IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
764 GreDeleteObject(Wnd->hrgnUpdate);
765 Wnd->hrgnUpdate = NULL;
766 }
767 }
768 }
769 }
770
771 Flags |= RDW_ERASE|RDW_FRAME; // For children.
772
773 }
774
775 if (!HadPaintMessage && IntIsWindowDirty(Wnd))
776 {
777 MsqIncPaintCountQueue(Wnd->head.pti);
778 }
779
780 } // The following flags are used to validate the window.
781 else if (Flags & (RDW_VALIDATE|RDW_NOINTERNALPAINT|RDW_NOERASE|RDW_NOFRAME))
782 {
783 if (Wnd->state & WNDS_UPDATEDIRTY && !(Flags & RDW_NOUPDATEDIRTY))
784 return;
785
786 if (Flags & RDW_NOINTERNALPAINT)
787 {
788 Wnd->state &= ~WNDS_INTERNALPAINT;
789 }
790
791 if (Flags & RDW_VALIDATE)
792 {
793 if (Flags & RDW_NOFRAME)
794 Wnd->state &= ~WNDS_SENDNCPAINT;
795
796 if (Flags & RDW_NOERASE)
797 Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
798
799 if (Wnd->hrgnUpdate > HRGN_WINDOW && RgnType != NULLREGION && Rgn > PRGN_WINDOW)
800 {
801 PREGION RgnUpdate = REGION_LockRgn(Wnd->hrgnUpdate);
802
803 if (RgnUpdate)
804 {
805 RgnType = IntGdiCombineRgn(RgnUpdate, RgnUpdate, Rgn, RGN_DIFF);
806 REGION_UnlockRgn(RgnUpdate);
807
808 if (RgnType == NULLREGION)
809 {
810 IntGdiSetRegionOwner(Wnd->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
811 GreDeleteObject(Wnd->hrgnUpdate);
812 Wnd->hrgnUpdate = NULL;
813 }
814 }
815 }
816 // If update is null, do not erase.
817 if (Wnd->hrgnUpdate == NULL)
818 {
819 Wnd->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
820 }
821 }
822
823 if (HadPaintMessage && !IntIsWindowDirty(Wnd))
824 {
825 MsqDecPaintCountQueue(Wnd->head.pti);
826 }
827 }
828
829 /*
830 * Process children if needed
831 */
832
833 if (!(Flags & RDW_NOCHILDREN) &&
834 !(Wnd->style & WS_MINIMIZE) &&
835 ((Flags & RDW_ALLCHILDREN) || !(Wnd->style & WS_CLIPCHILDREN)))
836 {
837 PWND Child;
838
839 for (Child = Wnd->spwndChild; Child; Child = Child->spwndNext)
840 {
841 if (Child->style & WS_VISIBLE)
842 {
843 /*
844 * Recursive call to update children hrgnUpdate
845 */
846 PREGION RgnTemp = IntSysCreateRectpRgn(0, 0, 0, 0);
847 if (RgnTemp)
848 {
849 if (Rgn > PRGN_WINDOW) IntGdiCombineRgn(RgnTemp, Rgn, 0, RGN_COPY);
850 IntInvalidateWindows(Child, ((Rgn > PRGN_WINDOW)?RgnTemp:Rgn), Flags);
851 REGION_Delete(RgnTemp);
852 }
853 }
854 }
855 }
856 TRACE("IntInvalidateWindows exit\n");
857 }
858
859 /*
860 * IntIsWindowDrawable
861 *
862 * Remarks
863 * Window is drawable when it is visible and all parents are not
864 * minimized.
865 */
866
867 BOOL FASTCALL
868 IntIsWindowDrawable(PWND Wnd)
869 {
870 PWND WndObject;
871
872 for (WndObject = Wnd; WndObject != NULL; WndObject = WndObject->spwndParent)
873 {
874 if ( WndObject->state2 & WNDS2_INDESTROY ||
875 WndObject->state & WNDS_DESTROYED ||
876 !WndObject ||
877 !(WndObject->style & WS_VISIBLE) ||
878 ((WndObject->style & WS_MINIMIZE) && (WndObject != Wnd)))
879 {
880 return FALSE;
881 }
882 }
883
884 return TRUE;
885 }
886
887 /*
888 * IntRedrawWindow
889 *
890 * Internal version of NtUserRedrawWindow that takes WND as
891 * first parameter.
892 */
893
894 BOOL FASTCALL
895 co_UserRedrawWindow(
896 PWND Window,
897 const RECTL* UpdateRect,
898 PREGION UpdateRgn,
899 ULONG Flags)
900 {
901 PREGION TmpRgn = NULL;
902 TRACE("co_UserRedrawWindow start Rgn %p\n",UpdateRgn);
903
904 /*
905 * Step 1.
906 * Validation of passed parameters.
907 */
908
909 if (!IntIsWindowDrawable(Window))
910 {
911 return TRUE; // Just do nothing!!!
912 }
913
914 if (Window == NULL)
915 {
916 Window = UserGetDesktopWindow();
917 }
918
919 /*
920 * Step 2.
921 * Transform the parameters UpdateRgn and UpdateRect into
922 * a region hRgn specified in screen coordinates.
923 */
924
925 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE)) // Both are OKAY!
926 {
927 /* We can't hold lock on GDI objects while doing roundtrips to user mode,
928 * so use a copy instead */
929 if (UpdateRgn)
930 {
931 TmpRgn = IntSysCreateRectpRgn(0, 0, 0, 0);
932
933 if (UpdateRgn > PRGN_WINDOW)
934 {
935 IntGdiCombineRgn(TmpRgn, UpdateRgn, NULL, RGN_COPY);
936 }
937
938 if (Window != UserGetDesktopWindow())
939 {
940 REGION_bOffsetRgn(TmpRgn, Window->rcClient.left, Window->rcClient.top);
941 }
942 }
943 else
944 {
945 if (UpdateRect != NULL)
946 {
947 if (Window == UserGetDesktopWindow())
948 {
949 TmpRgn = IntSysCreateRectpRgnIndirect(UpdateRect);
950 }
951 else
952 {
953 TmpRgn = IntSysCreateRectpRgn(Window->rcClient.left + UpdateRect->left,
954 Window->rcClient.top + UpdateRect->top,
955 Window->rcClient.left + UpdateRect->right,
956 Window->rcClient.top + UpdateRect->bottom);
957 }
958 }
959 else
960 {
961 if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
962 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
963 {
964 if (!RECTL_bIsEmptyRect(&Window->rcWindow))
965 TmpRgn = IntSysCreateRectpRgnIndirect(&Window->rcWindow);
966 }
967 else
968 {
969 if (!RECTL_bIsEmptyRect(&Window->rcClient))
970 TmpRgn = IntSysCreateRectpRgnIndirect(&Window->rcClient);
971 }
972 }
973 }
974 }
975
976 /* Fixes test RDW_INTERNALPAINT behavior */
977 if (TmpRgn == NULL)
978 {
979 TmpRgn = PRGN_WINDOW; // Need a region so the bits can be set!!!
980 }
981
982 /*
983 * Step 3.
984 * Adjust the window update region depending on hRgn and flags.
985 */
986
987 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT) &&
988 TmpRgn != NULL)
989 {
990 IntInvalidateWindows(Window, TmpRgn, Flags);
991 }
992
993 /*
994 * Step 4.
995 * Repaint and erase windows if needed.
996 */
997
998 if (Flags & RDW_UPDATENOW)
999 {
1000 UserUpdateWindows(Window, Flags);
1001 }
1002 else if (Flags & RDW_ERASENOW)
1003 {
1004 if ((Flags & (RDW_NOCHILDREN|RDW_ALLCHILDREN)) == 0)
1005 Flags |= RDW_CLIPCHILDREN;
1006
1007 UserSyncAndPaintWindows(Window, Flags);
1008 }
1009
1010 /*
1011 * Step 5.
1012 * Cleanup ;-)
1013 */
1014
1015 if (TmpRgn > PRGN_WINDOW)
1016 {
1017 REGION_Delete(TmpRgn);
1018 }
1019 TRACE("co_UserRedrawWindow exit\n");
1020
1021 return TRUE;
1022 }
1023
1024 VOID FASTCALL
1025 PaintSuspendedWindow(PWND pwnd, HRGN hrgnOrig)
1026 {
1027 if (pwnd->hrgnUpdate)
1028 {
1029 HDC hDC;
1030 INT Flags = DC_NC|DC_NOSENDMSG;
1031 HRGN hrgnTemp;
1032 RECT Rect;
1033 INT type;
1034 PREGION prgn;
1035
1036 if (pwnd->hrgnUpdate > HRGN_WINDOW)
1037 {
1038 hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
1039 type = NtGdiCombineRgn( hrgnTemp, pwnd->hrgnUpdate, 0, RGN_COPY);
1040 if (type == ERROR)
1041 {
1042 GreDeleteObject(hrgnTemp);
1043 hrgnTemp = HRGN_WINDOW;
1044 }
1045 }
1046 else
1047 {
1048 hrgnTemp = GreCreateRectRgnIndirect(&pwnd->rcWindow);
1049 }
1050
1051 if ( hrgnOrig &&
1052 hrgnTemp > HRGN_WINDOW &&
1053 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnOrig, RGN_AND) == NULLREGION)
1054 {
1055 GreDeleteObject(hrgnTemp);
1056 return;
1057 }
1058
1059 hDC = UserGetDCEx(pwnd, hrgnTemp, DCX_WINDOW|DCX_INTERSECTRGN|DCX_USESTYLE|DCX_KEEPCLIPRGN);
1060
1061 Rect = pwnd->rcWindow;
1062 RECTL_vOffsetRect(&Rect, -pwnd->rcWindow.left, -pwnd->rcWindow.top);
1063
1064 // Clear out client area!
1065 FillRect(hDC, &Rect, IntGetSysColorBrush(COLOR_WINDOW));
1066
1067 NC_DoNCPaint(pwnd, hDC, Flags); // Redraw without MENUs.
1068
1069 UserReleaseDC(pwnd, hDC, FALSE);
1070
1071 prgn = REGION_LockRgn(hrgnTemp);
1072 IntInvalidateWindows(pwnd, prgn, RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ALLCHILDREN);
1073 REGION_UnlockRgn(prgn);
1074
1075 // Set updates for this window.
1076 pwnd->state |= WNDS_SENDNCPAINT|WNDS_SENDERASEBACKGROUND|WNDS_UPDATEDIRTY;
1077
1078 // DCX_KEEPCLIPRGN is set. Check it anyway.
1079 if (hrgnTemp > HRGN_WINDOW && GreIsHandleValid(hrgnTemp)) GreDeleteObject(hrgnTemp);
1080 }
1081 }
1082
1083 VOID FASTCALL
1084 UpdateTheadChildren(PWND pWnd, HRGN hRgn)
1085 {
1086 PaintSuspendedWindow( pWnd, hRgn );
1087
1088 if (!(pWnd->style & WS_CLIPCHILDREN))
1089 return;
1090
1091 pWnd = pWnd->spwndChild; // invalidate children if any.
1092 while (pWnd)
1093 {
1094 UpdateTheadChildren( pWnd, hRgn );
1095 pWnd = pWnd->spwndNext;
1096 }
1097 }
1098
1099 VOID FASTCALL
1100 UpdateThreadWindows(PWND pWnd, PTHREADINFO pti, HRGN hRgn)
1101 {
1102 PWND pwndTemp;
1103
1104 for ( pwndTemp = pWnd;
1105 pwndTemp;
1106 pwndTemp = pwndTemp->spwndNext )
1107 {
1108 if (pwndTemp->head.pti == pti)
1109 {
1110 UserUpdateWindows(pwndTemp, RDW_ALLCHILDREN);
1111 }
1112 else
1113 {
1114 if (IsThreadSuspended(pwndTemp->head.pti) || MsqIsHung(pwndTemp->head.pti))
1115 {
1116 UpdateTheadChildren(pwndTemp, hRgn);
1117 }
1118 else
1119 UserUpdateWindows(pwndTemp, RDW_ALLCHILDREN);
1120 }
1121 }
1122 }
1123
1124 BOOL FASTCALL
1125 IntIsWindowDirty(PWND Wnd)
1126 {
1127 return ( Wnd->style & WS_VISIBLE &&
1128 ( Wnd->hrgnUpdate != NULL ||
1129 Wnd->state & WNDS_INTERNALPAINT ) );
1130 }
1131
1132 /*
1133 Conditions to paint any window:
1134
1135 1. Update region is not null.
1136 2. Internal paint flag is set.
1137 3. Paint count is not zero.
1138
1139 */
1140 PWND FASTCALL
1141 IntFindWindowToRepaint(PWND Window, PTHREADINFO Thread)
1142 {
1143 PWND hChild;
1144 PWND TempWindow;
1145
1146 for (; Window != NULL; Window = Window->spwndNext)
1147 {
1148 if (IntWndBelongsToThread(Window, Thread))
1149 {
1150 if (IntIsWindowDirty(Window))
1151 {
1152 /* Make sure all non-transparent siblings are already drawn. */
1153 if (Window->ExStyle & WS_EX_TRANSPARENT)
1154 {
1155 for (TempWindow = Window->spwndNext; TempWindow != NULL;
1156 TempWindow = TempWindow->spwndNext)
1157 {
1158 if (!(TempWindow->ExStyle & WS_EX_TRANSPARENT) &&
1159 IntWndBelongsToThread(TempWindow, Thread) &&
1160 IntIsWindowDirty(TempWindow))
1161 {
1162 return TempWindow;
1163 }
1164 }
1165 }
1166 return Window;
1167 }
1168 }
1169 /* find a child of the specified window that needs repainting */
1170 if (Window->spwndChild)
1171 {
1172 hChild = IntFindWindowToRepaint(Window->spwndChild, Thread);
1173 if (hChild != NULL)
1174 return hChild;
1175 }
1176 }
1177 return Window;
1178 }
1179
1180 //
1181 // Internal painting of windows.
1182 //
1183 VOID FASTCALL
1184 IntPaintWindow( PWND Window )
1185 {
1186 // Handle normal painting.
1187 co_IntPaintWindows( Window, RDW_NOCHILDREN, FALSE );
1188 }
1189
1190 BOOL FASTCALL
1191 IntGetPaintMessage(
1192 PWND Window,
1193 UINT MsgFilterMin,
1194 UINT MsgFilterMax,
1195 PTHREADINFO Thread,
1196 MSG *Message,
1197 BOOL Remove)
1198 {
1199 PWND PaintWnd, StartWnd;
1200
1201 if ((MsgFilterMin != 0 || MsgFilterMax != 0) &&
1202 (MsgFilterMin > WM_PAINT || MsgFilterMax < WM_PAINT))
1203 return FALSE;
1204
1205 if (Thread->TIF_flags & TIF_SYSTEMTHREAD )
1206 {
1207 ERR("WM_PAINT is in a System Thread!\n");
1208 }
1209
1210 StartWnd = UserGetDesktopWindow();
1211 PaintWnd = IntFindWindowToRepaint(StartWnd, Thread);
1212
1213 Message->hwnd = PaintWnd ? UserHMGetHandle(PaintWnd) : NULL;
1214
1215 if (Message->hwnd == NULL && Thread->cPaintsReady)
1216 {
1217 // Find note in window.c:"PAINTING BUG".
1218 ERR("WARNING SOMETHING HAS GONE WRONG: Thread marked as containing dirty windows, but no dirty windows found! Counts %u\n",Thread->cPaintsReady);
1219 /* Hack to stop spamming the debug log ! */
1220 Thread->cPaintsReady = 0;
1221 return FALSE;
1222 }
1223
1224 if (Message->hwnd == NULL)
1225 return FALSE;
1226
1227 if (!(Window == NULL ||
1228 PaintWnd == Window ||
1229 IntIsChildWindow(Window, PaintWnd))) /* check that it is a child of the specified parent */
1230 return FALSE;
1231
1232 if (PaintWnd->state & WNDS_INTERNALPAINT)
1233 {
1234 PaintWnd->state &= ~WNDS_INTERNALPAINT;
1235 if (!PaintWnd->hrgnUpdate)
1236 MsqDecPaintCountQueue(Thread);
1237 }
1238 PaintWnd->state2 &= ~WNDS2_STARTPAINT;
1239 PaintWnd->state &= ~WNDS_UPDATEDIRTY;
1240
1241 Window = PaintWnd;
1242 while( Window && Window != UserGetDesktopWindow())
1243 {
1244 // Role back and check for clip children, do not set if any.
1245 if (Window->spwndParent && !(Window->spwndParent->style & WS_CLIPCHILDREN))
1246 {
1247 PaintWnd->state2 |= WNDS2_WMPAINTSENT;
1248 }
1249 Window = Window->spwndParent;
1250 }
1251
1252 Message->wParam = Message->lParam = 0;
1253 Message->message = WM_PAINT;
1254 return TRUE;
1255 }
1256
1257 BOOL
1258 FASTCALL
1259 IntPrintWindow(
1260 PWND pwnd,
1261 HDC hdcBlt,
1262 UINT nFlags)
1263 {
1264 HDC hdcSrc;
1265 INT cx, cy, xSrc, ySrc;
1266
1267 if ( nFlags & PW_CLIENTONLY)
1268 {
1269 cx = pwnd->rcClient.right - pwnd->rcClient.left;
1270 cy = pwnd->rcClient.bottom - pwnd->rcClient.top;
1271 xSrc = pwnd->rcClient.left - pwnd->rcWindow.left;
1272 ySrc = pwnd->rcClient.top - pwnd->rcWindow.top;
1273 }
1274 else
1275 {
1276 cx = pwnd->rcWindow.right - pwnd->rcWindow.left;
1277 cy = pwnd->rcWindow.bottom - pwnd->rcWindow.top;
1278 xSrc = 0;
1279 ySrc = 0;
1280 }
1281
1282 // TODO: Setup Redirection for Print.
1283 return FALSE;
1284
1285 /* Update the window just incase. */
1286 co_IntUpdateWindows( pwnd, RDW_ALLCHILDREN, FALSE);
1287
1288 hdcSrc = UserGetDCEx( pwnd, NULL, DCX_CACHE|DCX_WINDOW);
1289 /* Print window to printer context. */
1290 NtGdiBitBlt( hdcBlt,
1291 0,
1292 0,
1293 cx,
1294 cy,
1295 hdcSrc,
1296 xSrc,
1297 ySrc,
1298 SRCCOPY,
1299 0,
1300 0);
1301
1302 UserReleaseDC( pwnd, hdcSrc, FALSE);
1303
1304 // TODO: Release Redirection from Print.
1305
1306 return TRUE;
1307 }
1308
1309 BOOL
1310 FASTCALL
1311 IntFlashWindowEx(PWND pWnd, PFLASHWINFO pfwi)
1312 {
1313 DWORD FlashState;
1314 UINT uCount = pfwi->uCount;
1315 BOOL Activate = FALSE, Ret = FALSE;
1316
1317 ASSERT(pfwi);
1318
1319 FlashState = (DWORD)UserGetProp(pWnd, AtomFlashWndState, TRUE);
1320
1321 if (FlashState == FLASHW_FINISHED)
1322 {
1323 // Cycle has finished, kill timer and set this to Stop.
1324 FlashState |= FLASHW_KILLSYSTIMER;
1325 pfwi->dwFlags = FLASHW_STOP;
1326 }
1327 else
1328 {
1329 if (FlashState)
1330 {
1331 if (pfwi->dwFlags == FLASHW_SYSTIMER)
1332 {
1333 // Called from system timer, restore flags, counts and state.
1334 pfwi->dwFlags = LOWORD(FlashState);
1335 uCount = HIWORD(FlashState);
1336 FlashState = MAKELONG(LOWORD(FlashState),0);
1337 }
1338 else
1339 {
1340 // Clean out the trash! Fix SeaMonkey crash after restart.
1341 FlashState = 0;
1342 }
1343 }
1344
1345 if (FlashState == 0)
1346 { // First time in cycle, setup flash state.
1347 if ( pWnd->state & WNDS_ACTIVEFRAME ||
1348 (pfwi->dwFlags & FLASHW_CAPTION && pWnd->style & (WS_BORDER|WS_DLGFRAME)))
1349 {
1350 FlashState = FLASHW_STARTED|FLASHW_ACTIVE;
1351 }
1352 }
1353
1354 // Set previous window state.
1355 Ret = !!(FlashState & FLASHW_ACTIVE);
1356
1357 if ( (pfwi->dwFlags & FLASHW_TIMERNOFG) == FLASHW_TIMERNOFG &&
1358 gpqForeground == pWnd->head.pti->MessageQueue )
1359 {
1360 // Flashing until foreground, set this to Stop.
1361 pfwi->dwFlags = FLASHW_STOP;
1362 }
1363 }
1364
1365 // Toggle activate flag.
1366 if ( pfwi->dwFlags == FLASHW_STOP )
1367 {
1368 if (gpqForeground && gpqForeground->spwndActive == pWnd)
1369 Activate = TRUE;
1370 else
1371 Activate = FALSE;
1372 }
1373 else
1374 {
1375 Activate = (FlashState & FLASHW_ACTIVE) == 0;
1376 }
1377
1378 if ( pfwi->dwFlags == FLASHW_STOP || pfwi->dwFlags & FLASHW_CAPTION )
1379 {
1380 co_IntSendMessage(UserHMGetHandle(pWnd), WM_NCACTIVATE, Activate, 0);
1381 }
1382
1383 // FIXME: Check for a Stop Sign here.
1384 if ( pfwi->dwFlags & FLASHW_TRAY )
1385 {
1386 // Need some shell work here too.
1387 TRACE("FIXME: Flash window no Tray support!\n");
1388 }
1389
1390 if ( pfwi->dwFlags == FLASHW_STOP )
1391 {
1392 if (FlashState & FLASHW_KILLSYSTIMER)
1393 {
1394 IntKillTimer(pWnd, ID_EVENT_SYSTIMER_FLASHWIN, TRUE);
1395 }
1396
1397 UserRemoveProp(pWnd, AtomFlashWndState, TRUE);
1398 }
1399 else
1400 { // Have a count and started, set timer.
1401 if ( uCount )
1402 {
1403 FlashState |= FLASHW_COUNT;
1404
1405 if (!(Activate ^ !!(FlashState & FLASHW_STARTED)))
1406 uCount--;
1407
1408 if (!(FlashState & FLASHW_KILLSYSTIMER))
1409 pfwi->dwFlags |= FLASHW_TIMER;
1410 }
1411
1412 if (pfwi->dwFlags & FLASHW_TIMER)
1413 {
1414 FlashState |= FLASHW_KILLSYSTIMER;
1415
1416 IntSetTimer( pWnd,
1417 ID_EVENT_SYSTIMER_FLASHWIN,
1418 pfwi->dwTimeout ? pfwi->dwTimeout : gpsi->dtCaretBlink,
1419 SystemTimerProc,
1420 TMRF_SYSTEM );
1421 }
1422
1423 if (FlashState & FLASHW_COUNT && uCount == 0)
1424 {
1425 // Keep spinning? Nothing else to do.
1426 FlashState = FLASHW_FINISHED;
1427 }
1428 else
1429 {
1430 // Save state and flags so this can be restored next time through.
1431 FlashState ^= (FlashState ^ -!!(Activate)) & FLASHW_ACTIVE;
1432 FlashState ^= (FlashState ^ pfwi->dwFlags) & (FLASHW_MASK & ~FLASHW_TIMER);
1433 }
1434 FlashState = MAKELONG(LOWORD(FlashState),uCount);
1435 UserSetProp(pWnd, AtomFlashWndState, (HANDLE)FlashState, TRUE);
1436 }
1437 return Ret;
1438 }
1439
1440 HDC FASTCALL
1441 IntBeginPaint(PWND Window, PPAINTSTRUCT Ps)
1442 {
1443 RECT Rect;
1444 INT type;
1445 BOOL Erase = FALSE;
1446
1447 co_UserHideCaret(Window);
1448
1449 Window->state2 |= WNDS2_STARTPAINT;
1450 Window->state &= ~WNDS_PAINTNOTPROCESSED;
1451
1452 if (Window->state & WNDS_SENDNCPAINT)
1453 {
1454 HRGN hRgn;
1455 // Application can keep update dirty.
1456 do
1457 {
1458 Window->state &= ~WNDS_UPDATEDIRTY;
1459 hRgn = IntGetNCUpdateRgn(Window, FALSE);
1460 IntSendNCPaint(Window, hRgn);
1461 if (hRgn > HRGN_WINDOW && GreIsHandleValid(hRgn))
1462 {
1463 /* NOTE: The region can already be deleted! */
1464 GreDeleteObject(hRgn);
1465 }
1466 }
1467 while(Window->state & WNDS_UPDATEDIRTY);
1468 }
1469 else
1470 {
1471 Window->state &= ~WNDS_UPDATEDIRTY;
1472 }
1473
1474 RtlZeroMemory(Ps, sizeof(PAINTSTRUCT));
1475
1476 if (Window->state2 & WNDS2_ENDPAINTINVALIDATE)
1477 {
1478 ERR("BP: Another thread invalidated this window\n");
1479 }
1480
1481 Ps->hdc = UserGetDCEx( Window,
1482 Window->hrgnUpdate,
1483 DCX_INTERSECTRGN | DCX_USESTYLE);
1484 if (!Ps->hdc)
1485 {
1486 return NULL;
1487 }
1488
1489 // If set, always clear flags out due to the conditions later on for sending the message.
1490 if (Window->state & WNDS_SENDERASEBACKGROUND)
1491 {
1492 Window->state &= ~(WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
1493 Erase = TRUE;
1494 }
1495
1496 if (Window->hrgnUpdate != NULL)
1497 {
1498 MsqDecPaintCountQueue(Window->head.pti);
1499 IntGdiSetRegionOwner(Window->hrgnUpdate, GDI_OBJ_HMGR_POWNED);
1500 /* The region is part of the dc now and belongs to the process! */
1501 Window->hrgnUpdate = NULL;
1502 }
1503 else
1504 {
1505 if (Window->state & WNDS_INTERNALPAINT)
1506 MsqDecPaintCountQueue(Window->head.pti);
1507 }
1508
1509 type = GdiGetClipBox(Ps->hdc, &Ps->rcPaint);
1510
1511 IntGetClientRect(Window, &Rect);
1512
1513 Window->state &= ~WNDS_INTERNALPAINT;
1514
1515 if ( Erase && // Set to erase,
1516 type != NULLREGION && // don't erase if the clip box is empty,
1517 (!(Window->pcls->style & CS_PARENTDC) || // not parent dc or
1518 RECTL_bIntersectRect( &Rect, &Rect, &Ps->rcPaint) ) ) // intersecting.
1519 {
1520 Ps->fErase = !co_IntSendMessage(UserHMGetHandle(Window), WM_ERASEBKGND, (WPARAM)Ps->hdc, 0);
1521 if ( Ps->fErase )
1522 {
1523 Window->state |= (WNDS_SENDERASEBACKGROUND|WNDS_ERASEBACKGROUND);
1524 }
1525 }
1526 else
1527 {
1528 Ps->fErase = FALSE;
1529 }
1530
1531 IntSendChildNCPaint(Window);
1532
1533 return Ps->hdc;
1534 }
1535
1536 BOOL FASTCALL
1537 IntEndPaint(PWND Wnd, PPAINTSTRUCT Ps)
1538 {
1539 HDC hdc = NULL;
1540
1541 hdc = Ps->hdc;
1542
1543 UserReleaseDC(Wnd, hdc, TRUE);
1544
1545 if (Wnd->state2 & WNDS2_ENDPAINTINVALIDATE)
1546 {
1547 ERR("EP: Another thread invalidated this window\n");
1548 Wnd->state2 &= ~WNDS2_ENDPAINTINVALIDATE;
1549 }
1550
1551 Wnd->state2 &= ~(WNDS2_WMPAINTSENT|WNDS2_STARTPAINT);
1552
1553 co_UserShowCaret(Wnd);
1554
1555 return TRUE;
1556 }
1557
1558 BOOL FASTCALL
1559 IntFillWindow(PWND pWndParent,
1560 PWND pWnd,
1561 HDC hDC,
1562 HBRUSH hBrush)
1563 {
1564 RECT Rect, Rect1;
1565 INT type;
1566
1567 if (!pWndParent)
1568 pWndParent = pWnd;
1569
1570 type = GdiGetClipBox(hDC, &Rect);
1571
1572 IntGetClientRect(pWnd, &Rect1);
1573
1574 if ( type != NULLREGION && // Clip box is not empty,
1575 (!(pWnd->pcls->style & CS_PARENTDC) || // not parent dc or
1576 RECTL_bIntersectRect( &Rect, &Rect, &Rect1) ) ) // intersecting.
1577 {
1578 POINT ppt;
1579 INT x = 0, y = 0;
1580
1581 if ( pWndParent != UserGetDesktopWindow())
1582 {
1583 x = pWndParent->rcClient.left - pWnd->rcClient.left;
1584 y = pWndParent->rcClient.top - pWnd->rcClient.top;
1585 }
1586
1587 GreSetBrushOrg(hDC, x, y, &ppt);
1588
1589 if ( hBrush < (HBRUSH)CTLCOLOR_MAX )
1590 hBrush = GetControlColor( pWndParent, pWnd, hDC, HandleToUlong(hBrush) + WM_CTLCOLORMSGBOX);
1591
1592 FillRect(hDC, &Rect, hBrush);
1593
1594 GreSetBrushOrg(hDC, ppt.x, ppt.y, NULL);
1595
1596 return TRUE;
1597 }
1598 else
1599 return FALSE;
1600 }
1601
1602 /* PUBLIC FUNCTIONS ***********************************************************/
1603
1604 /*
1605 * NtUserBeginPaint
1606 *
1607 * Status
1608 * @implemented
1609 */
1610
1611 HDC APIENTRY
1612 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs)
1613 {
1614 PWND Window = NULL;
1615 PAINTSTRUCT Ps;
1616 NTSTATUS Status;
1617 HDC hDC;
1618 USER_REFERENCE_ENTRY Ref;
1619 DECLARE_RETURN(HDC);
1620
1621 TRACE("Enter NtUserBeginPaint\n");
1622 UserEnterExclusive();
1623
1624 if (!(Window = UserGetWindowObject(hWnd)))
1625 {
1626 RETURN( NULL);
1627 }
1628
1629 UserRefObjectCo(Window, &Ref);
1630
1631 hDC = IntBeginPaint(Window, &Ps);
1632
1633 Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT));
1634 if (! NT_SUCCESS(Status))
1635 {
1636 SetLastNtError(Status);
1637 RETURN(NULL);
1638 }
1639
1640 RETURN(hDC);
1641
1642 CLEANUP:
1643 if (Window) UserDerefObjectCo(Window);
1644
1645 TRACE("Leave NtUserBeginPaint, ret=%p\n",_ret_);
1646 UserLeave();
1647 END_CLEANUP;
1648
1649 }
1650
1651 /*
1652 * NtUserEndPaint
1653 *
1654 * Status
1655 * @implemented
1656 */
1657
1658 BOOL APIENTRY
1659 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* pUnsafePs)
1660 {
1661 NTSTATUS Status = STATUS_SUCCESS;
1662 PWND Window = NULL;
1663 PAINTSTRUCT Ps;
1664 USER_REFERENCE_ENTRY Ref;
1665 DECLARE_RETURN(BOOL);
1666
1667 TRACE("Enter NtUserEndPaint\n");
1668 UserEnterExclusive();
1669
1670 if (!(Window = UserGetWindowObject(hWnd)))
1671 {
1672 RETURN(FALSE);
1673 }
1674
1675 UserRefObjectCo(Window, &Ref); // Here for the exception.
1676
1677 _SEH2_TRY
1678 {
1679 ProbeForRead(pUnsafePs, sizeof(*pUnsafePs), 1);
1680 RtlCopyMemory(&Ps, pUnsafePs, sizeof(PAINTSTRUCT));
1681 }
1682 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1683 {
1684 Status = _SEH2_GetExceptionCode();
1685 }
1686 _SEH2_END
1687 if (!NT_SUCCESS(Status))
1688 {
1689 RETURN(FALSE);
1690 }
1691
1692 RETURN(IntEndPaint(Window, &Ps));
1693
1694 CLEANUP:
1695 if (Window) UserDerefObjectCo(Window);
1696
1697 TRACE("Leave NtUserEndPaint, ret=%i\n",_ret_);
1698 UserLeave();
1699 END_CLEANUP;
1700 }
1701
1702 /*
1703 * FillWindow: Called from User; Dialog, Edit and ListBox procs during a WM_ERASEBKGND.
1704 */
1705 /*
1706 * @implemented
1707 */
1708 BOOL APIENTRY
1709 NtUserFillWindow(HWND hWndParent,
1710 HWND hWnd,
1711 HDC hDC,
1712 HBRUSH hBrush)
1713 {
1714 BOOL ret = FALSE;
1715 PWND pWnd, pWndParent = NULL;
1716 USER_REFERENCE_ENTRY Ref;
1717
1718 TRACE("Enter NtUserFillWindow\n");
1719 UserEnterExclusive();
1720
1721 if (!hDC)
1722 {
1723 goto Exit;
1724 }
1725
1726 if (!(pWnd = UserGetWindowObject(hWnd)))
1727 {
1728 goto Exit;
1729 }
1730
1731 if (hWndParent && !(pWndParent = UserGetWindowObject(hWndParent)))
1732 {
1733 goto Exit;
1734 }
1735
1736 UserRefObjectCo(pWnd, &Ref);
1737 ret = IntFillWindow( pWndParent, pWnd, hDC, hBrush );
1738 UserDerefObjectCo(pWnd);
1739
1740 Exit:
1741 TRACE("Leave NtUserFillWindow, ret=%i\n",ret);
1742 UserLeave();
1743 return ret;
1744 }
1745
1746 /*
1747 * @implemented
1748 */
1749 BOOL APIENTRY
1750 NtUserFlashWindowEx(IN PFLASHWINFO pfwi)
1751 {
1752 PWND pWnd;
1753 FLASHWINFO finfo = {0};
1754 BOOL Ret = FALSE;
1755
1756 UserEnterExclusive();
1757
1758 _SEH2_TRY
1759 {
1760 ProbeForRead(pfwi, sizeof(FLASHWINFO), 1);
1761 RtlCopyMemory(&finfo, pfwi, sizeof(FLASHWINFO));
1762 }
1763 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
1764 {
1765 SetLastNtError(_SEH2_GetExceptionCode());
1766 _SEH2_YIELD(goto Exit);
1767 }
1768 _SEH2_END
1769
1770 if (!( pWnd = ValidateHwndNoErr(finfo.hwnd)) ||
1771 finfo.cbSize != sizeof(FLASHWINFO) ||
1772 finfo.dwFlags & ~(FLASHW_ALL|FLASHW_TIMER|FLASHW_TIMERNOFG) )
1773 {
1774 EngSetLastError(ERROR_INVALID_PARAMETER);
1775 goto Exit;
1776 }
1777
1778 Ret = IntFlashWindowEx(pWnd, &finfo);
1779
1780 Exit:
1781 UserLeave();
1782 return Ret;
1783 }
1784
1785 /*
1786 GetUpdateRgn, this fails the same as the old one.
1787 */
1788 INT FASTCALL
1789 co_UserGetUpdateRgn(PWND Window, HRGN hRgn, BOOL bErase)
1790 {
1791 int RegionType;
1792 BOOL Type;
1793 RECTL Rect;
1794
1795 ASSERT_REFS_CO(Window);
1796
1797 if (bErase)
1798 {
1799 USER_REFERENCE_ENTRY Ref;
1800 UserRefObjectCo(Window, &Ref);
1801 co_IntPaintWindows(Window, RDW_NOCHILDREN, FALSE);
1802 UserDerefObjectCo(Window);
1803 }
1804
1805 Window->state &= ~WNDS_UPDATEDIRTY;
1806
1807 if (Window->hrgnUpdate == NULL)
1808 {
1809 NtGdiSetRectRgn(hRgn, 0, 0, 0, 0);
1810 return NULLREGION;
1811 }
1812
1813 Rect = Window->rcClient;
1814 Type = IntIntersectWithParents(Window, &Rect);
1815
1816 if (Window->hrgnUpdate == HRGN_WINDOW)
1817 {
1818 // Trap it out.
1819 ERR("GURn: Caller is passing Window Region 1\n");
1820 if (!Type)
1821 {
1822 NtGdiSetRectRgn(hRgn, 0, 0, 0, 0);
1823 return NULLREGION;
1824 }
1825
1826 RegionType = SIMPLEREGION;
1827
1828 if (Window != UserGetDesktopWindow()) // Window->fnid == FNID_DESKTOP
1829 {
1830 RECTL_vOffsetRect(&Rect,
1831 -Window->rcClient.left,
1832 -Window->rcClient.top);
1833 }
1834 GreSetRectRgnIndirect(hRgn, &Rect);
1835 }
1836 else
1837 {
1838 HRGN hrgnTemp = GreCreateRectRgnIndirect(&Rect);
1839
1840 RegionType = NtGdiCombineRgn(hRgn, hrgnTemp, Window->hrgnUpdate, RGN_AND);
1841
1842 if (RegionType == ERROR || RegionType == NULLREGION)
1843 {
1844 if (hrgnTemp) GreDeleteObject(hrgnTemp);
1845 NtGdiSetRectRgn(hRgn, 0, 0, 0, 0);
1846 return RegionType;
1847 }
1848
1849 if (Window != UserGetDesktopWindow()) // Window->fnid == FNID_DESKTOP
1850 {
1851 NtGdiOffsetRgn(hRgn,
1852 -Window->rcClient.left,
1853 -Window->rcClient.top);
1854 }
1855 if (hrgnTemp) GreDeleteObject(hrgnTemp);
1856 }
1857 return RegionType;
1858 }
1859
1860 BOOL FASTCALL
1861 co_UserGetUpdateRect(PWND Window, PRECT pRect, BOOL bErase)
1862 {
1863 INT RegionType;
1864 BOOL Ret = TRUE;
1865
1866 if (bErase)
1867 {
1868 USER_REFERENCE_ENTRY Ref;
1869 UserRefObjectCo(Window, &Ref);
1870 co_IntPaintWindows(Window, RDW_NOCHILDREN, FALSE);
1871 UserDerefObjectCo(Window);
1872 }
1873
1874 Window->state &= ~WNDS_UPDATEDIRTY;
1875
1876 if (Window->hrgnUpdate == NULL)
1877 {
1878 pRect->left = pRect->top = pRect->right = pRect->bottom = 0;
1879 Ret = FALSE;
1880 }
1881 else
1882 {
1883 /* Get the update region bounding box. */
1884 if (Window->hrgnUpdate == HRGN_WINDOW)
1885 {
1886 *pRect = Window->rcClient;
1887 ERR("GURt: Caller is retrieving Window Region 1\n");
1888 }
1889 else
1890 {
1891 RegionType = IntGdiGetRgnBox(Window->hrgnUpdate, pRect);
1892
1893 if (RegionType != ERROR && RegionType != NULLREGION)
1894 RECTL_bIntersectRect(pRect, pRect, &Window->rcClient);
1895 }
1896
1897 if (IntIntersectWithParents(Window, pRect))
1898 {
1899 if (Window != UserGetDesktopWindow()) // Window->fnid == FNID_DESKTOP
1900 {
1901 RECTL_vOffsetRect(pRect,
1902 -Window->rcClient.left,
1903 -Window->rcClient.top);
1904 }
1905 if (Window->pcls->style & CS_OWNDC)
1906 {
1907 HDC hdc;
1908 //DWORD layout;
1909 hdc = UserGetDCEx(Window, NULL, DCX_USESTYLE);
1910 //layout = NtGdiSetLayout(hdc, -1, 0);
1911 //IntMapWindowPoints( 0, Window, (LPPOINT)pRect, 2 );
1912 GreDPtoLP( hdc, (LPPOINT)pRect, 2 );
1913 //NtGdiSetLayout(hdc, -1, layout);
1914 UserReleaseDC(Window, hdc, FALSE);
1915 }
1916 }
1917 else
1918 {
1919 pRect->left = pRect->top = pRect->right = pRect->bottom = 0;
1920 }
1921 }
1922 return Ret;
1923 }
1924
1925 /*
1926 * NtUserGetUpdateRgn
1927 *
1928 * Status
1929 * @implemented
1930 */
1931
1932 INT APIENTRY
1933 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
1934 {
1935 DECLARE_RETURN(INT);
1936 PWND Window;
1937 INT ret;
1938
1939 TRACE("Enter NtUserGetUpdateRgn\n");
1940 UserEnterExclusive();
1941
1942 if (!(Window = UserGetWindowObject(hWnd)))
1943 {
1944 RETURN(ERROR);
1945 }
1946
1947 ret = co_UserGetUpdateRgn(Window, hRgn, bErase);
1948
1949 RETURN(ret);
1950
1951 CLEANUP:
1952 TRACE("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_);
1953 UserLeave();
1954 END_CLEANUP;
1955 }
1956
1957 /*
1958 * NtUserGetUpdateRect
1959 *
1960 * Status
1961 * @implemented
1962 */
1963
1964 BOOL APIENTRY
1965 NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase)
1966 {
1967 PWND Window;
1968 RECTL Rect;
1969 NTSTATUS Status;
1970 BOOL Ret;
1971 DECLARE_RETURN(BOOL);
1972
1973 TRACE("Enter NtUserGetUpdateRect\n");
1974 UserEnterExclusive();
1975
1976 if (!(Window = UserGetWindowObject(hWnd)))
1977 {
1978 RETURN(FALSE);
1979 }
1980
1981 Ret = co_UserGetUpdateRect(Window, &Rect, bErase);
1982
1983 if (UnsafeRect != NULL)
1984 {
1985 Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECTL));
1986 if (!NT_SUCCESS(Status))
1987 {
1988 EngSetLastError(ERROR_INVALID_PARAMETER);
1989 RETURN(FALSE);
1990 }
1991 }
1992
1993 RETURN(Ret);
1994
1995 CLEANUP:
1996 TRACE("Leave NtUserGetUpdateRect, ret=%i\n",_ret_);
1997 UserLeave();
1998 END_CLEANUP;
1999 }
2000
2001 /*
2002 * NtUserRedrawWindow
2003 *
2004 * Status
2005 * @implemented
2006 */
2007
2008 BOOL APIENTRY
2009 NtUserRedrawWindow(
2010 HWND hWnd,
2011 CONST RECT *lprcUpdate,
2012 HRGN hrgnUpdate,
2013 UINT flags)
2014 {
2015 RECTL SafeUpdateRect;
2016 PWND Wnd;
2017 BOOL Ret;
2018 USER_REFERENCE_ENTRY Ref;
2019 NTSTATUS Status = STATUS_SUCCESS;
2020 PREGION RgnUpdate = NULL;
2021 DECLARE_RETURN(BOOL);
2022
2023 TRACE("Enter NtUserRedrawWindow\n");
2024 UserEnterExclusive();
2025
2026 if (!(Wnd = UserGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
2027 {
2028 RETURN( FALSE);
2029 }
2030
2031 if (lprcUpdate)
2032 {
2033 _SEH2_TRY
2034 {
2035 ProbeForRead(lprcUpdate, sizeof(RECTL), 1);
2036 RtlCopyMemory(&SafeUpdateRect, lprcUpdate, sizeof(RECTL));
2037 }
2038 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2039 {
2040 Status = _SEH2_GetExceptionCode();
2041 }
2042 _SEH2_END
2043 if (!NT_SUCCESS(Status))
2044 {
2045 EngSetLastError(RtlNtStatusToDosError(Status));
2046 RETURN( FALSE);
2047 }
2048 }
2049
2050 if ( flags & ~(RDW_ERASE|RDW_FRAME|RDW_INTERNALPAINT|RDW_INVALIDATE|
2051 RDW_NOERASE|RDW_NOFRAME|RDW_NOINTERNALPAINT|RDW_VALIDATE|
2052 RDW_ERASENOW|RDW_UPDATENOW|RDW_ALLCHILDREN|RDW_NOCHILDREN) )
2053 {
2054 /* RedrawWindow fails only in case that flags are invalid */
2055 EngSetLastError(ERROR_INVALID_FLAGS);
2056 RETURN( FALSE);
2057 }
2058
2059 /* We can't hold lock on GDI objects while doing roundtrips to user mode,
2060 * so it will be copied.
2061 */
2062 if (hrgnUpdate > HRGN_WINDOW)
2063 {
2064 RgnUpdate = REGION_LockRgn(hrgnUpdate);
2065 if (!RgnUpdate)
2066 {
2067 EngSetLastError(ERROR_NOT_ENOUGH_MEMORY);
2068 RETURN(FALSE);
2069 }
2070 REGION_UnlockRgn(RgnUpdate);
2071 }
2072 else if (hrgnUpdate == HRGN_WINDOW) // Trap it out.
2073 {
2074 ERR("NTRW: Caller is passing Window Region 1\n");
2075 }
2076
2077 UserRefObjectCo(Wnd, &Ref);
2078
2079 Ret = co_UserRedrawWindow( Wnd,
2080 lprcUpdate ? &SafeUpdateRect : NULL,
2081 RgnUpdate,
2082 flags);
2083
2084 UserDerefObjectCo(Wnd);
2085
2086 RETURN( Ret);
2087
2088 CLEANUP:
2089 TRACE("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
2090 UserLeave();
2091 END_CLEANUP;
2092 }
2093
2094 BOOL
2095 UserDrawCaptionText(
2096 PWND pWnd,
2097 HDC hDc,
2098 const PUNICODE_STRING Text,
2099 const RECTL *lpRc,
2100 UINT uFlags,
2101 HFONT hFont)
2102 {
2103 HFONT hOldFont = NULL;
2104 COLORREF OldTextColor;
2105 NONCLIENTMETRICSW nclm;
2106 NTSTATUS Status;
2107 BOOLEAN bDeleteFont = FALSE;
2108 SIZE Size;
2109 BOOL Ret = TRUE;
2110 ULONG fit = 0, Length;
2111 RECTL r = *lpRc;
2112
2113 TRACE("UserDrawCaptionText: %wZ\n", Text);
2114
2115 nclm.cbSize = sizeof(nclm);
2116 if (!UserSystemParametersInfo(SPI_GETNONCLIENTMETRICS, nclm.cbSize, &nclm, 0))
2117 {
2118 ERR("UserSystemParametersInfo() failed!\n");
2119 return FALSE;
2120 }
2121
2122 if (!hFont)
2123 {
2124 if(uFlags & DC_SMALLCAP)
2125 Status = TextIntCreateFontIndirect(&nclm.lfSmCaptionFont, &hFont);
2126 else
2127 Status = TextIntCreateFontIndirect(&nclm.lfCaptionFont, &hFont);
2128
2129 if(!NT_SUCCESS(Status))
2130 {
2131 ERR("TextIntCreateFontIndirect() failed! Status: 0x%x\n", Status);
2132 return FALSE;
2133 }
2134
2135 bDeleteFont = TRUE;
2136 }
2137
2138 IntGdiSetBkMode(hDc, TRANSPARENT);
2139
2140 hOldFont = NtGdiSelectFont(hDc, hFont);
2141
2142 if(uFlags & DC_INBUTTON)
2143 OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(COLOR_BTNTEXT));
2144 else
2145 OldTextColor = IntGdiSetTextColor(hDc,
2146 IntGetSysColor(uFlags & DC_ACTIVE ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
2147
2148 // Adjust for system menu.
2149 if (pWnd && pWnd->style & WS_SYSMENU)
2150 {
2151 r.right -= UserGetSystemMetrics(SM_CYCAPTION) - 1;
2152 if ((pWnd->style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX)) && !(pWnd->ExStyle & WS_EX_TOOLWINDOW))
2153 {
2154 r.right -= UserGetSystemMetrics(SM_CXSIZE) + 1;
2155 r.right -= UserGetSystemMetrics(SM_CXSIZE) + 1;
2156 }
2157 }
2158
2159 GreGetTextExtentExW(hDc, Text->Buffer, Text->Length/sizeof(WCHAR), r.right - r.left, &fit, 0, &Size, 0);
2160
2161 Length = (Text->Length/sizeof(WCHAR) == fit ? fit : fit+1);
2162
2163 if (Text->Length/sizeof(WCHAR) > Length)
2164 {
2165 Ret = FALSE;
2166 }
2167
2168 if (Ret)
2169 { // Faster while in setup.
2170 GreExtTextOutW( hDc,
2171 lpRc->left,
2172 lpRc->top + (lpRc->bottom - lpRc->top) / 2 - Size.cy / 2, // DT_SINGLELINE && DT_VCENTER
2173 ETO_CLIPPED,
2174 (RECTL *)lpRc,
2175 Text->Buffer,
2176 Length,
2177 NULL,
2178 0 );
2179 }
2180 else
2181 {
2182 DrawTextW( hDc,
2183 Text->Buffer,
2184 Text->Length/sizeof(WCHAR),
2185 (RECTL *)&r,
2186 DT_END_ELLIPSIS|DT_SINGLELINE|DT_VCENTER|DT_NOPREFIX|DT_LEFT);
2187 }
2188
2189 IntGdiSetTextColor(hDc, OldTextColor);
2190
2191 if (hOldFont)
2192 NtGdiSelectFont(hDc, hOldFont);
2193
2194 if (bDeleteFont)
2195 GreDeleteObject(hFont);
2196
2197 return Ret;
2198 }
2199
2200 //
2201 // This draws Buttons, Icons and Text...
2202 //
2203 BOOL UserDrawCaption(
2204 PWND pWnd,
2205 HDC hDc,
2206 RECTL *lpRc,
2207 HFONT hFont,
2208 HICON hIcon,
2209 const PUNICODE_STRING Str,
2210 UINT uFlags)
2211 {
2212 BOOL Ret = FALSE;
2213 HBRUSH hBgBrush, hOldBrush = NULL;
2214 RECTL Rect = *lpRc;
2215 BOOL HasIcon;
2216
2217 RECTL_vMakeWellOrdered(lpRc);
2218
2219 /* Determine whether the icon needs to be displayed */
2220 if (!hIcon && pWnd != NULL)
2221 {
2222 HasIcon = (uFlags & DC_ICON) && !(uFlags & DC_SMALLCAP) &&
2223 (pWnd->style & WS_SYSMENU) && !(pWnd->ExStyle & WS_EX_TOOLWINDOW);
2224 }
2225 else
2226 HasIcon = (hIcon != NULL);
2227
2228 // Draw the caption background
2229 if((uFlags & DC_GRADIENT) && !(uFlags & DC_INBUTTON))
2230 {
2231 static GRADIENT_RECT gcap = {0, 1};
2232 TRIVERTEX Vertices[2];
2233 COLORREF Colors[2];
2234
2235 Colors[0] = IntGetSysColor((uFlags & DC_ACTIVE) ?
2236 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION);
2237
2238 Colors[1] = IntGetSysColor((uFlags & DC_ACTIVE) ?
2239 COLOR_GRADIENTACTIVECAPTION : COLOR_GRADIENTINACTIVECAPTION);
2240
2241 Vertices[0].x = Rect.left;
2242 Vertices[0].y = Rect.top;
2243 Vertices[0].Red = (WORD)Colors[0]<<8;
2244 Vertices[0].Green = (WORD)Colors[0] & 0xFF00;
2245 Vertices[0].Blue = (WORD)(Colors[0]>>8) & 0xFF00;
2246 Vertices[0].Alpha = 0;
2247
2248 Vertices[1].x = Rect.right;
2249 Vertices[1].y = Rect.bottom;
2250 Vertices[1].Red = (WORD)Colors[1]<<8;
2251 Vertices[1].Green = (WORD)Colors[1] & 0xFF00;
2252 Vertices[1].Blue = (WORD)(Colors[1]>>8) & 0xFF00;
2253 Vertices[1].Alpha = 0;
2254
2255 if(!GreGradientFill(hDc, Vertices, 2, &gcap, 1, GRADIENT_FILL_RECT_H))
2256 {
2257 ERR("GreGradientFill() failed!\n");
2258 goto cleanup;
2259 }
2260 }
2261 else
2262 {
2263 if(uFlags & DC_INBUTTON)
2264 hBgBrush = IntGetSysColorBrush(COLOR_3DFACE);
2265 else if(uFlags & DC_ACTIVE)
2266 hBgBrush = IntGetSysColorBrush(COLOR_ACTIVECAPTION);
2267 else
2268 hBgBrush = IntGetSysColorBrush(COLOR_INACTIVECAPTION);
2269
2270 hOldBrush = NtGdiSelectBrush(hDc, hBgBrush);
2271
2272 if(!hOldBrush)
2273 {
2274 ERR("NtGdiSelectBrush() failed!\n");
2275 goto cleanup;
2276 }
2277
2278 if(!NtGdiPatBlt(hDc, Rect.left, Rect.top,
2279 Rect.right - Rect.left,
2280 Rect.bottom - Rect.top,
2281 PATCOPY))
2282 {
2283 ERR("NtGdiPatBlt() failed!\n");
2284 goto cleanup;
2285 }
2286 }
2287
2288 /* Draw icon */
2289 if (HasIcon)
2290 {
2291 PCURICON_OBJECT pIcon = NULL;
2292
2293 if (hIcon)
2294 {
2295 pIcon = UserGetCurIconObject(hIcon);
2296 }
2297 else if (pWnd)
2298 {
2299 pIcon = NC_IconForWindow(pWnd);
2300 // FIXME: NC_IconForWindow should reference it for us */
2301 if (pIcon)
2302 UserReferenceObject(pIcon);
2303 }
2304
2305 if (pIcon)
2306 {
2307 LONG cx = UserGetSystemMetrics(SM_CXSMICON);
2308 LONG cy = UserGetSystemMetrics(SM_CYSMICON);
2309 LONG x = Rect.left - cx/2 + 1 + (Rect.bottom - Rect.top)/2; // this is really what Window does
2310 LONG y = (Rect.top + Rect.bottom - cy)/2; // center
2311 UserDrawIconEx(hDc, x, y, pIcon, cx, cy, 0, NULL, DI_NORMAL);
2312 UserDereferenceObject(pIcon);
2313 }
2314 else
2315 {
2316 HasIcon = FALSE;
2317 }
2318 }
2319
2320 if (HasIcon)
2321 Rect.left += Rect.bottom - Rect.top;
2322
2323 if((uFlags & DC_TEXT))
2324 {
2325 BOOL Set = FALSE;
2326 Rect.left += 2;
2327
2328 if (Str)
2329 Set = UserDrawCaptionText(pWnd, hDc, Str, &Rect, uFlags, hFont);
2330 else if (pWnd != NULL) // FIXME: Windows does not do that
2331 {
2332 UNICODE_STRING ustr;
2333 ustr.Buffer = pWnd->strName.Buffer; // FIXME: LARGE_STRING truncated!
2334 ustr.Length = (USHORT)min(pWnd->strName.Length, MAXUSHORT);
2335 ustr.MaximumLength = (USHORT)min(pWnd->strName.MaximumLength, MAXUSHORT);
2336 Set = UserDrawCaptionText(pWnd, hDc, &ustr, &Rect, uFlags, hFont);
2337 }
2338 if (pWnd)
2339 {
2340 if (Set)
2341 pWnd->state2 &= ~WNDS2_CAPTIONTEXTTRUNCATED;
2342 else
2343 pWnd->state2 |= WNDS2_CAPTIONTEXTTRUNCATED;
2344 }
2345 }
2346
2347 Ret = TRUE;
2348
2349 cleanup:
2350 if (hOldBrush) NtGdiSelectBrush(hDc, hOldBrush);
2351
2352 return Ret;
2353 }
2354
2355 INT
2356 FASTCALL
2357 UserRealizePalette(HDC hdc)
2358 {
2359 HWND hWnd, hWndDesktop;
2360 DWORD Ret;
2361
2362 Ret = IntGdiRealizePalette(hdc);
2363 if (Ret) // There was a change.
2364 {
2365 hWnd = IntWindowFromDC(hdc);
2366 if (hWnd) // Send broadcast if dc is associated with a window.
2367 { // FYI: Thread locked in CallOneParam.
2368 hWndDesktop = IntGetDesktopWindow();
2369 if ( hWndDesktop != hWnd )
2370 {
2371 PWND pWnd = UserGetWindowObject(hWndDesktop);
2372 ERR("RealizePalette Desktop.");
2373 hdc = UserGetWindowDC(pWnd);
2374 IntPaintDesktop(hdc);
2375 UserReleaseDC(pWnd,hdc,FALSE);
2376 }
2377 UserSendNotifyMessage((HWND)HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0);
2378 }
2379 }
2380 return Ret;
2381 }
2382
2383 BOOL
2384 APIENTRY
2385 NtUserDrawCaptionTemp(
2386 HWND hWnd,
2387 HDC hDC,
2388 LPCRECT lpRc,
2389 HFONT hFont,
2390 HICON hIcon,
2391 const PUNICODE_STRING str,
2392 UINT uFlags)
2393 {
2394 PWND pWnd = NULL;
2395 UNICODE_STRING SafeStr = {0};
2396 NTSTATUS Status = STATUS_SUCCESS;
2397 RECTL SafeRect;
2398 BOOL Ret;
2399
2400 UserEnterExclusive();
2401
2402 if (hWnd != NULL)
2403 {
2404 if(!(pWnd = UserGetWindowObject(hWnd)))
2405 {
2406 UserLeave();
2407 return FALSE;
2408 }
2409 }
2410
2411 _SEH2_TRY
2412 {
2413 ProbeForRead(lpRc, sizeof(RECTL), sizeof(ULONG));
2414 RtlCopyMemory(&SafeRect, lpRc, sizeof(RECTL));
2415 if (str != NULL)
2416 {
2417 SafeStr = ProbeForReadUnicodeString(str);
2418 if (SafeStr.Length != 0)
2419 {
2420 ProbeForRead( SafeStr.Buffer,
2421 SafeStr.Length,
2422 sizeof(WCHAR));
2423 }
2424 }
2425 }
2426 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
2427 {
2428 Status = _SEH2_GetExceptionCode();
2429 }
2430 _SEH2_END;
2431
2432 if (Status != STATUS_SUCCESS)
2433 {
2434 SetLastNtError(Status);
2435 UserLeave();
2436 return FALSE;
2437 }
2438
2439 if (str != NULL)
2440 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, &SafeStr, uFlags);
2441 else
2442 {
2443 if ( RECTL_bIsEmptyRect(&SafeRect) && hFont == 0 && hIcon == 0 )
2444 {
2445 Ret = TRUE;
2446 if (uFlags & DC_DRAWCAPTIONMD)
2447 {
2448 ERR("NC Caption Mode\n");
2449 UserDrawCaptionBar(pWnd, hDC, uFlags);
2450 goto Exit;
2451 }
2452 else if (uFlags & DC_DRAWFRAMEMD)
2453 {
2454 ERR("NC Paint Mode\n");
2455 NC_DoNCPaint(pWnd, hDC, uFlags); // Update Menus too!
2456 goto Exit;
2457 }
2458 }
2459 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, NULL, uFlags);
2460 }
2461 Exit:
2462 UserLeave();
2463 return Ret;
2464 }
2465
2466 BOOL
2467 APIENTRY
2468 NtUserDrawCaption(HWND hWnd,
2469 HDC hDC,
2470 LPCRECT lpRc,
2471 UINT uFlags)
2472 {
2473 return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags);
2474 }
2475
2476 BOOL
2477 APIENTRY
2478 NtUserInvalidateRect(
2479 HWND hWnd,
2480 CONST RECT *lpUnsafeRect,
2481 BOOL bErase)
2482 {
2483 UINT flags = RDW_INVALIDATE | (bErase ? RDW_ERASE : 0);
2484 if (!hWnd)
2485 {
2486 flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW;
2487 lpUnsafeRect = NULL;
2488 }
2489 return NtUserRedrawWindow(hWnd, lpUnsafeRect, NULL, flags);
2490 }
2491
2492 BOOL
2493 APIENTRY
2494 NtUserInvalidateRgn(
2495 HWND hWnd,
2496 HRGN hRgn,
2497 BOOL bErase)
2498 {
2499 if (!hWnd)
2500 {
2501 EngSetLastError( ERROR_INVALID_WINDOW_HANDLE );
2502 return FALSE;
2503 }
2504 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
2505 }
2506
2507 BOOL
2508 APIENTRY
2509 NtUserPrintWindow(
2510 HWND hwnd,
2511 HDC hdcBlt,
2512 UINT nFlags)
2513 {
2514 PWND Window;
2515 BOOL Ret = FALSE;
2516
2517 UserEnterExclusive();
2518
2519 if (hwnd)
2520 {
2521 if (!(Window = UserGetWindowObject(hwnd)) || // FIXME:
2522 Window == UserGetDesktopWindow() || // pWnd->fnid == FNID_DESKTOP
2523 Window == UserGetMessageWindow() ) // pWnd->fnid == FNID_MESSAGEWND
2524 {
2525 goto Exit;
2526 }
2527
2528 if ( Window )
2529 {
2530 /* Validate flags and check it as a mask for 0 or 1. */
2531 if ( (nFlags & PW_CLIENTONLY) == nFlags)
2532 Ret = IntPrintWindow( Window, hdcBlt, nFlags);
2533 else
2534 EngSetLastError(ERROR_INVALID_PARAMETER);
2535 }
2536 }
2537 Exit:
2538 UserLeave();
2539 return Ret;
2540 }
2541
2542 /* ValidateRect gets redirected to NtUserValidateRect:
2543 http://blog.csdn.net/ntdll/archive/2005/10/19/509299.aspx */
2544 BOOL
2545 APIENTRY
2546 NtUserValidateRect(
2547 HWND hWnd,
2548 const RECT *lpRect)
2549 {
2550 UINT flags = RDW_VALIDATE;
2551 if (!hWnd)
2552 {
2553 flags = RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_FRAME | RDW_ERASE | RDW_ERASENOW;
2554 lpRect = NULL;
2555 }
2556 return NtUserRedrawWindow(hWnd, lpRect, NULL, flags);
2557 }
2558
2559 /* EOF */