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