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