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