Sync to trunk head (35333)
[reactos.git] / reactos / subsystems / win32 / win32k / ntuser / painting.c
1 /*
2 * ReactOS W32 Subsystem
3 * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003 ReactOS Team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 *
19 * $Id$
20 *
21 * COPYRIGHT: See COPYING in the top level directory
22 * PROJECT: ReactOS kernel
23 * PURPOSE: Window painting function
24 * FILE: subsys/win32k/ntuser/painting.c
25 * PROGRAMER: Filip Navara (xnavara@volny.cz)
26 * REVISION HISTORY:
27 * 06/06/2001 Created (?)
28 * 18/11/2003 Complete rewrite
29 */
30
31 /* INCLUDES ******************************************************************/
32
33 #include <w32k.h>
34
35 #define NDEBUG
36 #include <debug.h>
37
38 /* PRIVATE FUNCTIONS **********************************************************/
39
40 /**
41 * @name IntIntersectWithParents
42 *
43 * Intersect window rectangle with all parent client rectangles.
44 *
45 * @param Child
46 * Pointer to child window to start intersecting from.
47 * @param WindowRect
48 * Pointer to rectangle that we want to intersect in screen
49 * coordinates on input and intersected rectangle on output (if TRUE
50 * is returned).
51 *
52 * @return
53 * If any parent is minimized or invisible or the resulting rectangle
54 * is empty then FALSE is returned. Otherwise TRUE is returned.
55 */
56
57 BOOL FASTCALL
58 IntIntersectWithParents(PWINDOW_OBJECT Child, PRECT WindowRect)
59 {
60 PWINDOW_OBJECT ParentWindow;
61 PWINDOW ParentWnd;
62
63 ParentWindow = Child->Parent;
64 while (ParentWindow != NULL)
65 {
66 ParentWnd = ParentWindow->Wnd;
67 if (!(ParentWnd->Style & WS_VISIBLE) ||
68 (ParentWnd->Style & WS_MINIMIZE))
69 {
70 return FALSE;
71 }
72
73 if (!IntGdiIntersectRect(WindowRect, WindowRect, &ParentWnd->ClientRect))
74 {
75 return FALSE;
76 }
77
78 /* FIXME: Layered windows. */
79
80 ParentWindow = ParentWindow->Parent;
81 }
82
83 return TRUE;
84 }
85
86 BOOL FASTCALL
87 IntValidateParent(PWINDOW_OBJECT Child, HRGN hValidateRgn, BOOL Recurse)
88 {
89 PWINDOW_OBJECT ParentWindow = Child->Parent;
90 PWINDOW ParentWnd;
91
92 while (ParentWindow)
93 {
94 ParentWnd = ParentWindow->Wnd;
95 if (ParentWnd->Style & WS_CLIPCHILDREN)
96 break;
97
98 if (ParentWindow->UpdateRegion != 0)
99 {
100 if (Recurse)
101 return FALSE;
102
103 IntInvalidateWindows(ParentWindow, hValidateRgn,
104 RDW_VALIDATE | RDW_NOCHILDREN);
105 }
106
107 ParentWindow = ParentWindow->Parent;
108 }
109
110 return TRUE;
111 }
112
113 /**
114 * @name IntCalcWindowRgn
115 *
116 * Get a window or client region.
117 */
118
119 HRGN FASTCALL
120 IntCalcWindowRgn(PWINDOW_OBJECT Window, BOOL Client)
121 {
122 PWINDOW Wnd;
123 HRGN hRgnWindow;
124 UINT RgnType;
125
126 Wnd = Window->Wnd;
127 if (Client)
128 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Wnd->ClientRect);
129 else
130 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Wnd->WindowRect);
131
132 if (Window->WindowRegion != NULL && !(Wnd->Style & WS_MINIMIZE))
133 {
134 NtGdiOffsetRgn(hRgnWindow,
135 -Wnd->WindowRect.left,
136 -Wnd->WindowRect.top);
137 RgnType = NtGdiCombineRgn(hRgnWindow, hRgnWindow, Window->WindowRegion, RGN_AND);
138 NtGdiOffsetRgn(hRgnWindow,
139 Wnd->WindowRect.left,
140 Wnd->WindowRect.top);
141 }
142
143 return hRgnWindow;
144 }
145
146 /**
147 * @name IntGetNCUpdateRgn
148 *
149 * Get non-client update region of a window and optionally validate it.
150 *
151 * @param Window
152 * Pointer to window to get the NC update region from.
153 * @param Validate
154 * Set to TRUE to force validating the NC update region.
155 *
156 * @return
157 * Handle to NC update region. The caller is responsible for deleting
158 * it.
159 */
160
161 HRGN FASTCALL
162 IntGetNCUpdateRgn(PWINDOW_OBJECT Window, BOOL Validate)
163 {
164 HRGN hRgnNonClient;
165 HRGN hRgnWindow;
166 UINT RgnType;
167
168 if (Window->UpdateRegion != NULL &&
169 Window->UpdateRegion != (HRGN)1)
170 {
171 hRgnNonClient = NtGdiCreateRectRgn(0, 0, 0, 0);
172
173 /*
174 * If region creation fails it's safe to fallback to whole
175 * window region.
176 */
177 if (hRgnNonClient == NULL)
178 {
179 return (HRGN)1;
180 }
181
182 hRgnWindow = IntCalcWindowRgn(Window, TRUE);
183 if (hRgnWindow == NULL)
184 {
185 NtGdiDeleteObject(hRgnNonClient);
186 return (HRGN)1;
187 }
188
189 RgnType = NtGdiCombineRgn(hRgnNonClient, Window->UpdateRegion,
190 hRgnWindow, RGN_DIFF);
191 if (RgnType == ERROR)
192 {
193 NtGdiDeleteObject(hRgnWindow);
194 NtGdiDeleteObject(hRgnNonClient);
195 return (HRGN)1;
196 }
197 else if (RgnType == NULLREGION)
198 {
199 NtGdiDeleteObject(hRgnWindow);
200 NtGdiDeleteObject(hRgnNonClient);
201 return NULL;
202 }
203
204 /*
205 * Remove the nonclient region from the standard update region if
206 * we were asked for it.
207 */
208
209 if (Validate)
210 {
211 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
212 hRgnWindow, RGN_AND) == NULLREGION)
213 {
214 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
215 NtGdiDeleteObject(Window->UpdateRegion);
216 Window->UpdateRegion = NULL;
217 if (!(Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT))
218 MsqDecPaintCountQueue(Window->MessageQueue);
219 }
220 }
221
222 NtGdiDeleteObject(hRgnWindow);
223
224 return hRgnNonClient;
225 }
226 else
227 {
228 return Window->UpdateRegion;
229 }
230 }
231
232 /*
233 * IntPaintWindows
234 *
235 * Internal function used by IntRedrawWindow.
236 */
237
238 VOID FASTCALL
239 co_IntPaintWindows(PWINDOW_OBJECT Window, ULONG Flags, BOOL Recurse)
240 {
241 HDC hDC;
242 HWND hWnd = Window->hSelf;
243 HRGN TempRegion;
244 PWINDOW Wnd;
245
246 Wnd = Window->Wnd;
247
248 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
249 {
250 if (Window->UpdateRegion)
251 {
252 if (!IntValidateParent(Window, Window->UpdateRegion, Recurse))
253 return;
254 }
255
256 if (Flags & RDW_UPDATENOW)
257 {
258 if (Window->UpdateRegion != NULL ||
259 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
260 {
261 co_IntSendMessage(hWnd, WM_PAINT, 0, 0);
262 }
263 }
264 else
265 {
266 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
267 {
268 TempRegion = IntGetNCUpdateRgn(Window, TRUE);
269 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
270 MsqDecPaintCountQueue(Window->MessageQueue);
271 co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)TempRegion, 0);
272 if ((HANDLE) 1 != TempRegion && NULL != TempRegion)
273 {
274 /* NOTE: The region can already be deleted! */
275 GDIOBJ_FreeObjByHandle(TempRegion, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
276 }
277 }
278
279 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
280 {
281 if (Window->UpdateRegion)
282 {
283 hDC = UserGetDCEx(Window, Window->UpdateRegion,
284 DCX_CACHE | DCX_USESTYLE |
285 DCX_INTERSECTRGN | DCX_KEEPCLIPRGN);
286 if (co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
287 {
288 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
289 }
290 UserReleaseDC(Window, hDC, FALSE);
291 }
292 }
293 }
294 }
295
296 /*
297 * Check that the window is still valid at this point
298 */
299 if (!IntIsWindow(hWnd))
300 {
301 return;
302 }
303
304 /*
305 * Paint child windows.
306 */
307 if (!(Flags & RDW_NOCHILDREN) && !(Wnd->Style & WS_MINIMIZE) &&
308 ((Flags & RDW_ALLCHILDREN) || !(Wnd->Style & WS_CLIPCHILDREN)))
309 {
310 HWND *List, *phWnd;
311
312 if ((List = IntWinListChildren(Window)))
313 {
314 /* FIXME: Handle WS_EX_TRANSPARENT */
315 for (phWnd = List; *phWnd; ++phWnd)
316 {
317 Window = UserGetWindowObject(*phWnd);
318 Wnd = Window->Wnd;
319 if (Window && (Wnd->Style & WS_VISIBLE))
320 {
321 USER_REFERENCE_ENTRY Ref;
322 UserRefObjectCo(Window, &Ref);
323 co_IntPaintWindows(Window, Flags, TRUE);
324 UserDerefObjectCo(Window);
325 }
326 }
327 ExFreePool(List);
328 }
329 }
330 }
331
332 /*
333 * IntInvalidateWindows
334 *
335 * Internal function used by IntRedrawWindow.
336 */
337
338 VOID FASTCALL
339 IntInvalidateWindows(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags)
340 {
341 INT RgnType;
342 PWINDOW Wnd;
343 BOOL HadPaintMessage, HadNCPaintMessage;
344 BOOL HasPaintMessage, HasNCPaintMessage;
345
346 Wnd = Window->Wnd;
347
348 /*
349 * If the nonclient is not to be redrawn, clip the region to the client
350 * rect
351 */
352 if (0 != (Flags & RDW_INVALIDATE) && 0 == (Flags & RDW_FRAME))
353 {
354 HRGN hRgnClient;
355
356 hRgnClient = UnsafeIntCreateRectRgnIndirect(&Window->Wnd->ClientRect);
357 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnClient, RGN_AND);
358 NtGdiDeleteObject(hRgnClient);
359 }
360
361 /*
362 * Clip the given region with window rectangle (or region)
363 */
364
365 if (!Window->WindowRegion || (Wnd->Style & WS_MINIMIZE))
366 {
367 HRGN hRgnWindow;
368
369 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->Wnd->WindowRect);
370 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnWindow, RGN_AND);
371 NtGdiDeleteObject(hRgnWindow);
372 }
373 else
374 {
375 NtGdiOffsetRgn(hRgn,
376 -Wnd->WindowRect.left,
377 -Wnd->WindowRect.top);
378 RgnType = NtGdiCombineRgn(hRgn, hRgn, Window->WindowRegion, RGN_AND);
379 NtGdiOffsetRgn(hRgn,
380 Wnd->WindowRect.left,
381 Wnd->WindowRect.top);
382 }
383
384 /*
385 * Save current state of pending updates
386 */
387
388 HadPaintMessage = Window->UpdateRegion != NULL ||
389 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
390 HadNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
391
392 /*
393 * Update the region and flags
394 */
395
396 if (Flags & RDW_INVALIDATE && RgnType != NULLREGION)
397 {
398 if (Window->UpdateRegion == NULL)
399 {
400 Window->UpdateRegion = NtGdiCreateRectRgn(0, 0, 0, 0);
401 GDIOBJ_SetOwnership(Window->UpdateRegion, NULL);
402 }
403
404 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
405 hRgn, RGN_OR) == NULLREGION)
406 {
407 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
408 NtGdiDeleteObject(Window->UpdateRegion);
409 Window->UpdateRegion = NULL;
410 }
411
412 if (Flags & RDW_FRAME)
413 Window->Flags |= WINDOWOBJECT_NEED_NCPAINT;
414 if (Flags & RDW_ERASE)
415 Window->Flags |= WINDOWOBJECT_NEED_ERASEBKGND;
416
417 Flags |= RDW_FRAME;
418 }
419
420 if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
421 {
422 if (Window->UpdateRegion != NULL)
423 {
424 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
425 hRgn, RGN_DIFF) == NULLREGION)
426 {
427 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
428 NtGdiDeleteObject(Window->UpdateRegion);
429 Window->UpdateRegion = NULL;
430 }
431 }
432
433 if (Window->UpdateRegion == NULL)
434 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
435 if (Flags & RDW_NOFRAME)
436 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
437 if (Flags & RDW_NOERASE)
438 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
439 }
440
441 if (Flags & RDW_INTERNALPAINT)
442 {
443 Window->Flags |= WINDOWOBJECT_NEED_INTERNALPAINT;
444 }
445
446 if (Flags & RDW_NOINTERNALPAINT)
447 {
448 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
449 }
450
451 /*
452 * Process children if needed
453 */
454
455 if (!(Flags & RDW_NOCHILDREN) && !(Wnd->Style & WS_MINIMIZE) &&
456 ((Flags & RDW_ALLCHILDREN) || !(Wnd->Style & WS_CLIPCHILDREN)))
457 {
458 PWINDOW_OBJECT Child;
459
460 for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
461 {
462 if (Child->Wnd->Style & WS_VISIBLE)
463 {
464 /*
465 * Recursive call to update children UpdateRegion
466 */
467 HRGN hRgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
468 NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
469 IntInvalidateWindows(Child, hRgnTemp, Flags);
470 NtGdiDeleteObject(hRgnTemp);
471 }
472
473 }
474 }
475
476 /*
477 * Fake post paint messages to window message queue if needed
478 */
479
480 HasPaintMessage = Window->UpdateRegion != NULL ||
481 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
482 HasNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
483
484 if (HasPaintMessage != HadPaintMessage)
485 {
486 if (HadPaintMessage)
487 MsqDecPaintCountQueue(Window->MessageQueue);
488 else
489 MsqIncPaintCountQueue(Window->MessageQueue);
490 }
491
492 if (HasNCPaintMessage != HadNCPaintMessage)
493 {
494 if (HadNCPaintMessage)
495 MsqDecPaintCountQueue(Window->MessageQueue);
496 else
497 MsqIncPaintCountQueue(Window->MessageQueue);
498 }
499
500 }
501
502 /*
503 * IntIsWindowDrawable
504 *
505 * Remarks
506 * Window is drawable when it is visible and all parents are not
507 * minimized.
508 */
509
510 BOOL FASTCALL
511 IntIsWindowDrawable(PWINDOW_OBJECT Window)
512 {
513 PWINDOW_OBJECT WndObject;
514 PWINDOW Wnd;
515
516 for (WndObject = Window; WndObject != NULL; WndObject = WndObject->Parent)
517 {
518 Wnd = WndObject->Wnd;
519 if (!(Wnd->Style & WS_VISIBLE) ||
520 ((Wnd->Style & WS_MINIMIZE) && (WndObject != Window)))
521 {
522 return FALSE;
523 }
524 }
525
526 return TRUE;
527 }
528
529 /*
530 * IntRedrawWindow
531 *
532 * Internal version of NtUserRedrawWindow that takes WINDOW_OBJECT as
533 * first parameter.
534 */
535
536 BOOL FASTCALL
537 co_UserRedrawWindow(PWINDOW_OBJECT Window, const RECT* UpdateRect, HRGN UpdateRgn,
538 ULONG Flags)
539 {
540 HRGN hRgn = NULL;
541
542 /*
543 * Step 1.
544 * Validation of passed parameters.
545 */
546
547 if (!IntIsWindowDrawable(Window) ||
548 (Flags & (RDW_VALIDATE | RDW_INVALIDATE)) ==
549 (RDW_VALIDATE | RDW_INVALIDATE))
550 {
551 return FALSE;
552 }
553
554 /*
555 * Step 2.
556 * Transform the parameters UpdateRgn and UpdateRect into
557 * a region hRgn specified in screen coordinates.
558 */
559
560 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE))
561 {
562 if (UpdateRgn != NULL)
563 {
564 hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
565 if (NtGdiCombineRgn(hRgn, UpdateRgn, NULL, RGN_COPY) == NULLREGION)
566 {
567 NtGdiDeleteObject(hRgn);
568 hRgn = NULL;
569 }
570 else
571 NtGdiOffsetRgn(hRgn, Window->Wnd->ClientRect.left, Window->Wnd->ClientRect.top);
572 }
573 else if (UpdateRect != NULL)
574 {
575 if (!IntGdiIsEmptyRect(UpdateRect))
576 {
577 hRgn = UnsafeIntCreateRectRgnIndirect((RECT *)UpdateRect);
578 NtGdiOffsetRgn(hRgn, Window->Wnd->ClientRect.left, Window->Wnd->ClientRect.top);
579 }
580 }
581 else if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
582 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
583 {
584 if (!IntGdiIsEmptyRect(&Window->Wnd->WindowRect))
585 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->Wnd->WindowRect);
586 }
587 else
588 {
589 if (!IntGdiIsEmptyRect(&Window->Wnd->ClientRect))
590 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->Wnd->ClientRect);
591 }
592 }
593
594 /*
595 * Step 3.
596 * Adjust the window update region depending on hRgn and flags.
597 */
598
599 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT) &&
600 hRgn != NULL)
601 {
602 IntInvalidateWindows(Window, hRgn, Flags);
603 }
604
605 /*
606 * Step 4.
607 * Repaint and erase windows if needed.
608 */
609
610 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
611 {
612 co_IntPaintWindows(Window, Flags, FALSE);
613 }
614
615 /*
616 * Step 5.
617 * Cleanup ;-)
618 */
619
620 if (hRgn != NULL)
621 {
622 NtGdiDeleteObject(hRgn);
623 }
624
625 return TRUE;
626 }
627
628 BOOL FASTCALL
629 IntIsWindowDirty(PWINDOW_OBJECT Window)
630 {
631 PWINDOW Wnd = Window->Wnd;
632 return (Wnd->Style & WS_VISIBLE) &&
633 ((Window->UpdateRegion != NULL) ||
634 (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT) ||
635 (Window->Flags & WINDOWOBJECT_NEED_NCPAINT));
636 }
637
638 HWND FASTCALL
639 IntFindWindowToRepaint(PWINDOW_OBJECT Window, PW32THREAD Thread)
640 {
641 HWND hChild;
642 PWINDOW_OBJECT TempWindow;
643 PWINDOW Wnd, TempWnd;
644
645 for (; Window != NULL; Window = Window->NextSibling)
646 {
647 Wnd = Window->Wnd;
648 if (IntWndBelongsToThread(Window, Thread) &&
649 IntIsWindowDirty(Window))
650 {
651 /* Make sure all non-transparent siblings are already drawn. */
652 if (Wnd->ExStyle & WS_EX_TRANSPARENT)
653 {
654 for (TempWindow = Window->NextSibling; TempWindow != NULL;
655 TempWindow = TempWindow->NextSibling)
656 {
657 TempWnd = TempWindow->Wnd;
658 if (!(TempWnd->ExStyle & WS_EX_TRANSPARENT) &&
659 IntWndBelongsToThread(TempWindow, Thread) &&
660 IntIsWindowDirty(TempWindow))
661 {
662 return TempWindow->hSelf;
663 }
664 }
665 }
666
667 return Window->hSelf;
668 }
669
670 if (Window->FirstChild)
671 {
672 hChild = IntFindWindowToRepaint(Window->FirstChild, Thread);
673 if (hChild != NULL)
674 return hChild;
675 }
676 }
677
678 return NULL;
679 }
680
681 BOOL FASTCALL
682 IntGetPaintMessage(HWND hWnd, UINT MsgFilterMin, UINT MsgFilterMax,
683 PW32THREAD Thread, MSG *Message, BOOL Remove)
684 {
685 PUSER_MESSAGE_QUEUE MessageQueue = (PUSER_MESSAGE_QUEUE)Thread->MessageQueue;
686
687 if (!MessageQueue->PaintCount)
688 return FALSE;
689
690 if ((MsgFilterMin != 0 || MsgFilterMax != 0) &&
691 (MsgFilterMin > WM_PAINT || MsgFilterMax < WM_PAINT))
692 return FALSE;
693
694 Message->hwnd = IntFindWindowToRepaint(UserGetDesktopWindow(), PsGetCurrentThreadWin32Thread());
695
696 if (Message->hwnd == NULL)
697 {
698 DPRINT1("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found!\n");
699 /* Hack to stop spamming the debuglog ! */
700 MessageQueue->PaintCount = 0;
701 return FALSE;
702 }
703
704 if (hWnd != NULL && Message->hwnd != hWnd)
705 return FALSE;
706
707 Message->message = WM_PAINT;
708 Message->wParam = Message->lParam = 0;
709
710 return TRUE;
711 }
712
713 static
714 HWND FASTCALL
715 co_IntFixCaret(PWINDOW_OBJECT Window, LPRECT lprc, UINT flags)
716 {
717 PDESKTOP_OBJECT Desktop;
718 PTHRDCARETINFO CaretInfo;
719 HWND hWndCaret;
720 PWINDOW_OBJECT WndCaret;
721
722 ASSERT_REFS_CO(Window);
723
724 Desktop = ((PW32THREAD)PsGetCurrentThread()->Tcb.Win32Thread)->Desktop;
725 CaretInfo = ((PUSER_MESSAGE_QUEUE)Desktop->ActiveMessageQueue)->CaretInfo;
726 hWndCaret = CaretInfo->hWnd;
727
728 WndCaret = UserGetWindowObject(hWndCaret);
729
730 //fix: check for WndCaret can be null
731 if (WndCaret == Window ||
732 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(Window, WndCaret)))
733 {
734 POINT pt, FromOffset, ToOffset, Offset;
735 RECT rcCaret;
736
737 pt.x = CaretInfo->Pos.x;
738 pt.y = CaretInfo->Pos.y;
739 IntGetClientOrigin(WndCaret, &FromOffset);
740 IntGetClientOrigin(Window, &ToOffset);
741 Offset.x = FromOffset.x - ToOffset.x;
742 Offset.y = FromOffset.y - ToOffset.y;
743 rcCaret.left = pt.x;
744 rcCaret.top = pt.y;
745 rcCaret.right = pt.x + CaretInfo->Size.cx;
746 rcCaret.bottom = pt.y + CaretInfo->Size.cy;
747 if (IntGdiIntersectRect(lprc, lprc, &rcCaret))
748 {
749 co_UserHideCaret(0);
750 lprc->left = pt.x;
751 lprc->top = pt.y;
752 return hWndCaret;
753 }
754 }
755
756 return 0;
757 }
758
759 /* PUBLIC FUNCTIONS ***********************************************************/
760
761 /*
762 * NtUserBeginPaint
763 *
764 * Status
765 * @implemented
766 */
767
768 HDC STDCALL
769 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs)
770 {
771 PWINDOW_OBJECT Window = NULL;
772 PAINTSTRUCT Ps;
773 NTSTATUS Status;
774 DECLARE_RETURN(HDC);
775 USER_REFERENCE_ENTRY Ref;
776 PWINDOW Wnd;
777
778 DPRINT("Enter NtUserBeginPaint\n");
779 UserEnterExclusive();
780
781 if (!(Window = UserGetWindowObject(hWnd)))
782 {
783 RETURN( NULL);
784 }
785
786 UserRefObjectCo(Window, &Ref);
787
788 Wnd = Window->Wnd;
789
790 co_UserHideCaret(Window);
791
792 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
793 {
794 HRGN hRgn;
795
796 hRgn = IntGetNCUpdateRgn(Window, FALSE);
797 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
798 MsqDecPaintCountQueue(Window->MessageQueue);
799 co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0);
800 if (hRgn != (HANDLE)1 && hRgn != NULL)
801 {
802 /* NOTE: The region can already by deleted! */
803 GDIOBJ_FreeObjByHandle(hRgn, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
804 }
805 }
806
807 RtlZeroMemory(&Ps, sizeof(PAINTSTRUCT));
808
809 Ps.hdc = UserGetDCEx(Window, Window->UpdateRegion, DCX_INTERSECTRGN | DCX_USESTYLE);
810 if (!Ps.hdc)
811 {
812 RETURN(NULL);
813 }
814
815 if (Window->UpdateRegion != NULL)
816 {
817 MsqDecPaintCountQueue(Window->MessageQueue);
818 GdiGetClipBox(Ps.hdc, &Ps.rcPaint);
819 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
820 /* The region is part of the dc now and belongs to the process! */
821 Window->UpdateRegion = NULL;
822 }
823 else
824 {
825 if (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
826 MsqDecPaintCountQueue(Window->MessageQueue);
827
828 IntGetClientRect(Window, &Ps.rcPaint);
829 }
830
831 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
832
833 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
834 {
835 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
836 Ps.fErase = !co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)Ps.hdc, 0);
837 }
838 else
839 {
840 Ps.fErase = FALSE;
841 }
842 if (Window->UpdateRegion)
843 {
844 if (!(Wnd->Style & WS_CLIPCHILDREN))
845 {
846 PWINDOW_OBJECT Child;
847 for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
848 {
849 IntInvalidateWindows(Child, Window->UpdateRegion, RDW_FRAME | RDW_ERASE | RDW_INVALIDATE | RDW_ALLCHILDREN);
850 }
851 }
852 }
853
854 Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT));
855 if (! NT_SUCCESS(Status))
856 {
857 SetLastNtError(Status);
858 RETURN(NULL);
859 }
860
861 RETURN(Ps.hdc);
862
863 CLEANUP:
864 if (Window) UserDerefObjectCo(Window);
865
866 DPRINT("Leave NtUserBeginPaint, ret=%i\n",_ret_);
867 UserLeave();
868 END_CLEANUP;
869
870 }
871
872 /*
873 * NtUserEndPaint
874 *
875 * Status
876 * @implemented
877 */
878
879 BOOL STDCALL
880 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* pUnsafePs)
881 {
882 NTSTATUS Status = STATUS_SUCCESS;
883 PWINDOW_OBJECT Window;
884 DECLARE_RETURN(BOOL);
885 USER_REFERENCE_ENTRY Ref;
886 HDC hdc = NULL;
887
888 DPRINT("Enter NtUserEndPaint\n");
889 UserEnterExclusive();
890
891 if (!(Window = UserGetWindowObject(hWnd)))
892 {
893 RETURN(FALSE);
894 }
895
896 _SEH_TRY
897 {
898 ProbeForRead(pUnsafePs, sizeof(*pUnsafePs), 1);
899 hdc = pUnsafePs->hdc;
900 }
901 _SEH_HANDLE
902 {
903 Status = _SEH_GetExceptionCode();
904 }
905 _SEH_END
906 if (!NT_SUCCESS(Status))
907 {
908 RETURN(FALSE);
909 }
910
911 UserReleaseDC(Window, hdc, TRUE);
912
913 UserRefObjectCo(Window, &Ref);
914 co_UserShowCaret(Window);
915 UserDerefObjectCo(Window);
916
917 RETURN(TRUE);
918
919 CLEANUP:
920 DPRINT("Leave NtUserEndPaint, ret=%i\n",_ret_);
921 UserLeave();
922 END_CLEANUP;
923 }
924
925
926 INT FASTCALL
927 co_UserGetUpdateRgn(PWINDOW_OBJECT Window, HRGN hRgn, BOOL bErase)
928 {
929 int RegionType;
930 RECT Rect;
931
932 ASSERT_REFS_CO(Window);
933
934 if (Window->UpdateRegion == NULL)
935 {
936 RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
937 }
938 else
939 {
940 Rect = Window->Wnd->ClientRect;
941 IntIntersectWithParents(Window, &Rect);
942 NtGdiSetRectRgn(hRgn, Rect.left, Rect.top, Rect.right, Rect.bottom);
943 RegionType = NtGdiCombineRgn(hRgn, hRgn, Window->UpdateRegion, RGN_AND);
944 NtGdiOffsetRgn(hRgn, -Window->Wnd->ClientRect.left, -Window->Wnd->ClientRect.top);
945 }
946
947 if (bErase && RegionType != NULLREGION && RegionType != ERROR)
948 {
949 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
950 }
951
952 return RegionType;
953 }
954
955 /*
956 * NtUserGetUpdateRgn
957 *
958 * Status
959 * @implemented
960 */
961
962 INT STDCALL
963 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
964 {
965 DECLARE_RETURN(INT);
966 PWINDOW_OBJECT Window;
967 INT ret;
968 USER_REFERENCE_ENTRY Ref;
969
970 DPRINT("Enter NtUserGetUpdateRgn\n");
971 UserEnterExclusive();
972
973 if (!(Window = UserGetWindowObject(hWnd)))
974 {
975 RETURN(ERROR);
976 }
977
978 UserRefObjectCo(Window, &Ref);
979 ret = co_UserGetUpdateRgn(Window, hRgn, bErase);
980 UserDerefObjectCo(Window);
981
982 RETURN(ret);
983
984 CLEANUP:
985 DPRINT("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_);
986 UserLeave();
987 END_CLEANUP;
988 }
989
990 /*
991 * NtUserGetUpdateRect
992 *
993 * Status
994 * @implemented
995 */
996
997 BOOL STDCALL
998 NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase)
999 {
1000 PWINDOW_OBJECT Window;
1001 RECT Rect;
1002 INT RegionType;
1003 PROSRGNDATA RgnData;
1004 NTSTATUS Status;
1005 DECLARE_RETURN(BOOL);
1006
1007 DPRINT("Enter NtUserGetUpdateRect\n");
1008 UserEnterExclusive();
1009
1010 if (!(Window = UserGetWindowObject(hWnd)))
1011 {
1012 RETURN(FALSE);
1013 }
1014
1015 if (Window->UpdateRegion == NULL)
1016 {
1017 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
1018 }
1019 else
1020 {
1021 /* Get the update region bounding box. */
1022 if (Window->UpdateRegion == (HRGN)1)
1023 {
1024 Rect = Window->Wnd->ClientRect;
1025 }
1026 else
1027 {
1028 RgnData = REGION_LockRgn(Window->UpdateRegion);
1029 ASSERT(RgnData != NULL);
1030 RegionType = REGION_GetRgnBox(RgnData, &Rect);
1031 REGION_UnlockRgn(RgnData);
1032
1033 if (RegionType != ERROR && RegionType != NULLREGION)
1034 IntGdiIntersectRect(&Rect, &Rect, &Window->Wnd->ClientRect);
1035 }
1036
1037 if (IntIntersectWithParents(Window, &Rect))
1038 {
1039 IntGdiOffsetRect(&Rect,
1040 -Window->Wnd->ClientRect.left,
1041 -Window->Wnd->ClientRect.top);
1042 } else
1043 {
1044 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
1045 }
1046 }
1047
1048 if (bErase && !IntGdiIsEmptyRect(&Rect))
1049 {
1050 USER_REFERENCE_ENTRY Ref;
1051 UserRefObjectCo(Window, &Ref);
1052 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
1053 UserDerefObjectCo(Window);
1054 }
1055
1056 if (UnsafeRect != NULL)
1057 {
1058 Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECT));
1059 if (!NT_SUCCESS(Status))
1060 {
1061 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1062 RETURN(FALSE);
1063 }
1064 }
1065
1066 RETURN(!IntGdiIsEmptyRect(&Rect));
1067
1068 CLEANUP:
1069 DPRINT("Leave NtUserGetUpdateRect, ret=%i\n",_ret_);
1070 UserLeave();
1071 END_CLEANUP;
1072 }
1073
1074 /*
1075 * NtUserRedrawWindow
1076 *
1077 * Status
1078 * @implemented
1079 */
1080
1081 BOOL STDCALL
1082 NtUserRedrawWindow(HWND hWnd, CONST RECT *lprcUpdate, HRGN hrgnUpdate,
1083 UINT flags)
1084 {
1085 RECT SafeUpdateRect;
1086 NTSTATUS Status;
1087 PWINDOW_OBJECT Wnd;
1088 DECLARE_RETURN(BOOL);
1089 USER_REFERENCE_ENTRY Ref;
1090
1091 DPRINT("Enter NtUserRedrawWindow\n");
1092 UserEnterExclusive();
1093
1094 if (!(Wnd = UserGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
1095 {
1096 RETURN( FALSE);
1097 }
1098
1099 if (lprcUpdate != NULL)
1100 {
1101 Status = MmCopyFromCaller(&SafeUpdateRect, (PRECT)lprcUpdate,
1102 sizeof(RECT));
1103
1104 if (!NT_SUCCESS(Status))
1105 {
1106 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1107 RETURN( FALSE);
1108 }
1109 }
1110
1111 UserRefObjectCo(Wnd, &Ref);
1112
1113 Status = co_UserRedrawWindow(Wnd, NULL == lprcUpdate ? NULL : &SafeUpdateRect,
1114 hrgnUpdate, flags);
1115
1116 UserDerefObjectCo(Wnd);
1117
1118 if (!NT_SUCCESS(Status))
1119 {
1120 /* IntRedrawWindow fails only in case that flags are invalid */
1121 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1122 RETURN( FALSE);
1123 }
1124
1125 RETURN( TRUE);
1126
1127 CLEANUP:
1128 DPRINT("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
1129 UserLeave();
1130 END_CLEANUP;
1131 }
1132
1133
1134
1135 static
1136 INT FASTCALL
1137 UserScrollDC(HDC hDC, INT dx, INT dy, const RECT *prcScroll,
1138 const RECT *prcClip, HRGN hrgnUpdate, LPRECT prcUpdate)
1139 {
1140 PDC pDC;
1141 RECT rcScroll, rcClip, rcSrc, rcDst;
1142 INT Result;
1143
1144 GdiGetClipBox(hDC, &rcClip);
1145 rcScroll = rcClip;
1146 if (prcClip)
1147 {
1148 IntGdiIntersectRect(&rcClip, &rcClip, prcClip);
1149 }
1150
1151 if (prcScroll)
1152 {
1153 rcScroll = *prcScroll;
1154 IntGdiIntersectRect(&rcSrc, &rcClip, prcScroll);
1155 }
1156 else
1157 {
1158 rcSrc = rcClip;
1159 }
1160
1161 rcDst = rcSrc;
1162 IntGdiOffsetRect(&rcDst, dx, dy);
1163 IntGdiIntersectRect(&rcDst, &rcDst, &rcClip);
1164
1165 if (!NtGdiBitBlt(hDC, rcDst.left, rcDst.top,
1166 rcDst.right - rcDst.left, rcDst.bottom - rcDst.top,
1167 hDC, rcDst.left - dx, rcDst.top - dy, SRCCOPY, 0, 0))
1168 {
1169 return ERROR;
1170 }
1171
1172 /* Calculate the region that was invalidated by moving or
1173 could not be copied, because it was not visible */
1174 if (hrgnUpdate || prcUpdate)
1175 {
1176 HRGN hrgnOwn, hrgnVisible, hrgnTmp;
1177
1178 pDC = DC_LockDc(hDC);
1179 if (!pDC)
1180 {
1181 return FALSE;
1182 }
1183 hrgnVisible = pDC->w.hVisRgn; // pDC->w.hGCClipRgn?
1184 DC_UnlockDc(pDC);
1185
1186 /* Begin with the shifted and then clipped scroll rect */
1187 rcDst = rcScroll;
1188 IntGdiOffsetRect(&rcDst, dx, dy);
1189 IntGdiIntersectRect(&rcDst, &rcDst, &rcClip);
1190 if (hrgnUpdate)
1191 {
1192 hrgnOwn = hrgnUpdate;
1193 if (!NtGdiSetRectRgn(hrgnOwn, rcDst.left, rcDst.top, rcDst.right, rcDst.bottom))
1194 {
1195 return ERROR;
1196 }
1197 }
1198 else
1199 {
1200 hrgnOwn = UnsafeIntCreateRectRgnIndirect(&rcDst);
1201 }
1202
1203 /* Add the source rect */
1204 hrgnTmp = UnsafeIntCreateRectRgnIndirect(&rcSrc);
1205 NtGdiCombineRgn(hrgnOwn, hrgnOwn, hrgnTmp, RGN_OR);
1206
1207 /* Substract the part of the dest that was visible in source */
1208 NtGdiCombineRgn(hrgnTmp, hrgnTmp, hrgnVisible, RGN_AND);
1209 NtGdiOffsetRgn(hrgnTmp, dx, dy);
1210 Result = NtGdiCombineRgn(hrgnOwn, hrgnOwn, hrgnTmp, RGN_DIFF);
1211
1212 NtGdiDeleteObject(hrgnTmp);
1213
1214 if (prcUpdate)
1215 {
1216 IntGdiGetRgnBox(hrgnOwn, prcUpdate);
1217 }
1218
1219 if (!hrgnUpdate)
1220 {
1221 NtGdiDeleteObject(hrgnOwn);
1222 }
1223 }
1224 else
1225 Result = NULLREGION;
1226
1227 return Result;
1228 }
1229
1230
1231
1232
1233 /*
1234 * NtUserScrollDC
1235 *
1236 * Status
1237 * @implemented
1238 */
1239
1240 BOOL STDCALL
1241 NtUserScrollDC(HDC hDC, INT dx, INT dy, const RECT *prcUnsafeScroll,
1242 const RECT *prcUnsafeClip, HRGN hrgnUpdate, LPRECT prcUnsafeUpdate)
1243 {
1244 DECLARE_RETURN(DWORD);
1245 RECT rcScroll, rcClip, rcUpdate;
1246 NTSTATUS Status = STATUS_SUCCESS;
1247 DWORD Result;
1248
1249 DPRINT("Enter NtUserScrollDC\n");
1250 UserEnterExclusive();
1251
1252 _SEH_TRY
1253 {
1254 if (prcUnsafeScroll)
1255 {
1256 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1257 rcScroll = *prcUnsafeScroll;
1258 }
1259 if (prcUnsafeClip)
1260 {
1261 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1262 rcClip = *prcUnsafeClip;
1263 }
1264 if (prcUnsafeUpdate)
1265 {
1266 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
1267 }
1268 }
1269 _SEH_HANDLE
1270 {
1271 Status = _SEH_GetExceptionCode();
1272 }
1273 _SEH_END
1274 if (!NT_SUCCESS(Status))
1275 {
1276 SetLastNtError(Status);
1277 RETURN(FALSE);
1278 }
1279
1280 Result = UserScrollDC(hDC, dx, dy,
1281 prcUnsafeScroll? &rcScroll : 0,
1282 prcUnsafeClip? &rcClip : 0, hrgnUpdate,
1283 prcUnsafeUpdate? &rcUpdate : NULL);
1284 if(Result == ERROR)
1285 {
1286 /* FIXME: Only if hRgnUpdate is invalid we should SetLastError(ERROR_INVALID_HANDLE) */
1287 RETURN(FALSE);
1288 }
1289
1290 if (prcUnsafeUpdate)
1291 {
1292 _SEH_TRY
1293 {
1294 *prcUnsafeUpdate = rcUpdate;
1295 }
1296 _SEH_HANDLE
1297 {
1298 Status = _SEH_GetExceptionCode();
1299 }
1300 _SEH_END
1301 if (!NT_SUCCESS(Status))
1302 {
1303 /* FIXME: SetLastError? */
1304 /* FIXME: correct? We have already scrolled! */
1305 RETURN(FALSE);
1306 }
1307 }
1308
1309 RETURN(TRUE);
1310
1311 CLEANUP:
1312 DPRINT("Leave NtUserScrollDC, ret=%i\n",_ret_);
1313 UserLeave();
1314 END_CLEANUP;
1315 }
1316
1317 /*
1318 * NtUserScrollWindowEx
1319 *
1320 * Status
1321 * @implemented
1322 */
1323
1324 DWORD STDCALL
1325 NtUserScrollWindowEx(HWND hWnd, INT dx, INT dy, const RECT *prcUnsafeScroll,
1326 const RECT *prcUnsafeClip, HRGN hrgnUpdate, LPRECT prcUnsafeUpdate, UINT flags)
1327 {
1328 RECT rcScroll, rcClip, rcCaret, rcUpdate;
1329 INT Result;
1330 PWINDOW_OBJECT Window = NULL, CaretWnd;
1331 HDC hDC;
1332 HRGN hrgnOwn = NULL, hrgnTemp;
1333 HWND hwndCaret;
1334 NTSTATUS Status = STATUS_SUCCESS;
1335 DECLARE_RETURN(DWORD);
1336 USER_REFERENCE_ENTRY Ref, CaretRef;
1337
1338 DPRINT("Enter NtUserScrollWindowEx\n");
1339 UserEnterExclusive();
1340
1341 Window = UserGetWindowObject(hWnd);
1342 if (!Window || !IntIsWindowDrawable(Window))
1343 {
1344 Window = NULL; /* prevent deref at cleanup */
1345 RETURN( ERROR);
1346 }
1347 UserRefObjectCo(Window, &Ref);
1348
1349 IntGetClientRect(Window, &rcClip);
1350
1351 _SEH_TRY
1352 {
1353 if (prcUnsafeScroll)
1354 {
1355 ProbeForRead(prcUnsafeScroll, sizeof(*prcUnsafeScroll), 1);
1356 IntGdiIntersectRect(&rcScroll, &rcClip, prcUnsafeScroll);
1357 }
1358 else
1359 rcScroll = rcClip;
1360
1361 if (prcUnsafeClip)
1362 {
1363 ProbeForRead(prcUnsafeClip, sizeof(*prcUnsafeClip), 1);
1364 IntGdiIntersectRect(&rcClip, &rcClip, prcUnsafeClip);
1365 }
1366 }
1367 _SEH_HANDLE
1368 {
1369 Status = _SEH_GetExceptionCode();
1370 }
1371 _SEH_END
1372
1373 if (!NT_SUCCESS(Status))
1374 {
1375 SetLastNtError(Status);
1376 RETURN(ERROR);
1377 }
1378
1379 if (rcClip.right <= rcClip.left || rcClip.bottom <= rcClip.top ||
1380 (dx == 0 && dy == 0))
1381 {
1382 RETURN(NULLREGION);
1383 }
1384
1385 if (hrgnUpdate)
1386 hrgnOwn = hrgnUpdate;
1387 else
1388 hrgnOwn = NtGdiCreateRectRgn(0, 0, 0, 0);
1389
1390 hDC = UserGetDCEx(Window, 0, DCX_CACHE | DCX_USESTYLE);
1391 if (!hDC)
1392 {
1393 /* FIXME: SetLastError? */
1394 RETURN(ERROR);
1395 }
1396
1397 rcCaret = rcScroll;
1398 hwndCaret = co_IntFixCaret(Window, &rcCaret, flags);
1399
1400 Result = UserScrollDC(hDC, dx, dy, &rcScroll, &rcClip, hrgnOwn, prcUnsafeUpdate? &rcUpdate : NULL);
1401 UserReleaseDC(Window, hDC, FALSE);
1402
1403 /*
1404 * Take into account the fact that some damage may have occurred during
1405 * the scroll.
1406 */
1407
1408 hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
1409 if (co_UserGetUpdateRgn(Window, hrgnTemp, FALSE) != NULLREGION)
1410 {
1411 HRGN hrgnClip = UnsafeIntCreateRectRgnIndirect(&rcClip);
1412 NtGdiOffsetRgn(hrgnTemp, dx, dy);
1413 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
1414 co_UserRedrawWindow(Window, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
1415 NtGdiDeleteObject(hrgnClip);
1416 }
1417 NtGdiDeleteObject(hrgnTemp);
1418
1419 if (flags & SW_SCROLLCHILDREN)
1420 {
1421 PWINDOW_OBJECT Child;
1422 RECT rcChild;
1423 POINT ClientOrigin;
1424 USER_REFERENCE_ENTRY WndRef;
1425 RECT rcDummy;
1426
1427 IntGetClientOrigin(Window, &ClientOrigin);
1428 for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
1429 {
1430 rcChild = Child->Wnd->WindowRect;
1431 rcChild.left -= ClientOrigin.x;
1432 rcChild.top -= ClientOrigin.y;
1433 rcChild.right -= ClientOrigin.x;
1434 rcChild.bottom -= ClientOrigin.y;
1435
1436 if (! prcUnsafeScroll || IntGdiIntersectRect(&rcDummy, &rcChild, &rcScroll))
1437 {
1438 UserRefObjectCo(Child, &WndRef);
1439 co_WinPosSetWindowPos(Child, 0, rcChild.left + dx, rcChild.top + dy, 0, 0,
1440 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1441 SWP_NOREDRAW);
1442 UserDerefObjectCo(Child);
1443 }
1444 }
1445 }
1446
1447 if (flags & (SW_INVALIDATE | SW_ERASE))
1448 {
1449 co_UserRedrawWindow(Window, NULL, hrgnOwn, RDW_INVALIDATE | RDW_ERASE |
1450 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
1451 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
1452 }
1453
1454 if ((CaretWnd = UserGetWindowObject(hwndCaret)))
1455 {
1456 UserRefObjectCo(CaretWnd, &CaretRef);
1457
1458 co_IntSetCaretPos(rcCaret.left + dx, rcCaret.top + dy);
1459 co_UserShowCaret(CaretWnd);
1460
1461 UserDerefObjectCo(CaretWnd);
1462 }
1463
1464 if (prcUnsafeUpdate)
1465 {
1466 _SEH_TRY
1467 {
1468 /* Probe here, to not fail on invalid pointer before scrolling */
1469 ProbeForWrite(prcUnsafeUpdate, sizeof(*prcUnsafeUpdate), 1);
1470 *prcUnsafeUpdate = rcUpdate;
1471 }
1472 _SEH_HANDLE
1473 {
1474 Status = _SEH_GetExceptionCode();
1475 }
1476 _SEH_END
1477
1478 if (!NT_SUCCESS(Status))
1479 {
1480 SetLastNtError(Status);
1481 RETURN(ERROR);
1482 }
1483 }
1484
1485 RETURN(Result);
1486
1487 CLEANUP:
1488 if (hrgnOwn && !hrgnUpdate)
1489 {
1490 NtGdiDeleteObject(hrgnOwn);
1491 }
1492
1493 if (Window)
1494 UserDerefObjectCo(Window);
1495
1496 DPRINT("Leave NtUserScrollWindowEx, ret=%i\n",_ret_);
1497 UserLeave();
1498 END_CLEANUP;
1499 }
1500
1501
1502 BOOL
1503 UserDrawSysMenuButton(
1504 PWINDOW_OBJECT pWnd,
1505 HDC hDc,
1506 LPRECT lpRc,
1507 BOOL Down)
1508 {
1509 HICON hIcon;
1510 PCURICON_OBJECT pIcon;
1511
1512 ASSERT(pWnd && lpRc);
1513
1514 /* Get the icon to draw. We don't care about WM_GETICON here. */
1515
1516 hIcon = pWnd->Wnd->Class->hIconSm;
1517
1518 if(!hIcon)
1519 {
1520 DPRINT("Wnd class has no small icon.\n");
1521 hIcon = pWnd->Wnd->Class->hIcon;
1522 }
1523
1524 if(!hIcon)
1525 {
1526 DPRINT("Wnd class hasn't any icon.\n");
1527 //FIXME: Draw "winlogo" icon.
1528 return FALSE;
1529 }
1530
1531 if(!(pIcon = UserGetCurIconObject(hIcon)))
1532 {
1533 DPRINT1("UserGetCurIconObject() failed!\n");
1534 return FALSE;
1535 }
1536
1537 return UserDrawIconEx(hDc, lpRc->left, lpRc->top, pIcon,
1538 UserGetSystemMetrics(SM_CXSMICON),
1539 UserGetSystemMetrics(SM_CYSMICON),
1540 0, NULL, DI_NORMAL);
1541 }
1542
1543 BOOL
1544 UserDrawCaptionText(HDC hDc,
1545 const PUNICODE_STRING Text,
1546 const LPRECT lpRc,
1547 UINT uFlags)
1548 {
1549 HFONT hOldFont = NULL, hFont = NULL;
1550 COLORREF OldTextColor;
1551 NONCLIENTMETRICSW nclm;
1552 NTSTATUS Status;
1553 #ifndef NDEBUG
1554 INT i;
1555 DPRINT("%s:", __FUNCTION__);
1556 for(i = 0; i < Text->Length/sizeof(WCHAR); i++)
1557 DbgPrint("%C", Text->Buffer[i]);
1558 DbgPrint(", %d\n", Text->Length/sizeof(WCHAR));
1559 #endif
1560
1561 nclm.cbSize = sizeof(nclm);
1562 if(!IntSystemParametersInfo(SPI_GETNONCLIENTMETRICS,
1563 sizeof(NONCLIENTMETRICS), &nclm, 0))
1564 {
1565 DPRINT1("%s: IntSystemParametersInfo() failed!\n", __FUNCTION__);
1566 return FALSE;
1567 }
1568
1569 IntGdiSetBkMode(hDc, TRANSPARENT);
1570
1571 if(uFlags & DC_SMALLCAP)
1572 Status = TextIntCreateFontIndirect(&nclm.lfSmCaptionFont, &hFont);
1573 else Status = TextIntCreateFontIndirect(&nclm.lfCaptionFont, &hFont);
1574
1575 if(!NT_SUCCESS(Status))
1576 {
1577 DPRINT1("%s: TextIntCreateFontIndirect() failed! Status: 0x%x\n",
1578 __FUNCTION__, Status);
1579 return FALSE;
1580 }
1581
1582 hOldFont = NtGdiSelectFont(hDc, hFont);
1583 if(!hOldFont)
1584 {
1585 DPRINT1("%s: SelectFont() failed!\n", __FUNCTION__);
1586 NtGdiDeleteObject(hFont);
1587 return FALSE;
1588 }
1589
1590 if(uFlags & DC_INBUTTON)
1591 OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(COLOR_BTNTEXT));
1592 else OldTextColor = IntGdiSetTextColor(hDc, IntGetSysColor(uFlags & DC_ACTIVE
1593 ? COLOR_CAPTIONTEXT : COLOR_INACTIVECAPTIONTEXT));
1594
1595 //FIXME: If string doesn't fit to rc, truncate it and add ellipsis.
1596
1597 NtGdiExtTextOutW(hDc, lpRc->left,
1598 lpRc->top, 0, NULL, Text->Buffer,
1599 Text->Length/sizeof(WCHAR), NULL, 0);
1600
1601 IntGdiSetTextColor(hDc, OldTextColor);
1602 NtGdiSelectFont(hDc, hOldFont);
1603 NtGdiDeleteObject(hFont);
1604
1605 return TRUE;
1606 }
1607
1608 BOOL UserDrawCaption(
1609 PWINDOW_OBJECT pWnd,
1610 HDC hDc,
1611 LPCRECT lpRc,
1612 HFONT hFont,
1613 HICON hIcon,
1614 const PUNICODE_STRING str,
1615 UINT uFlags)
1616 {
1617 BOOL Ret = FALSE;
1618 HBITMAP hMemBmp = NULL, hOldBmp = NULL;
1619 HBRUSH hOldBrush = NULL;
1620 HDC hMemDc = NULL;
1621 ULONG Height;
1622 UINT VCenter = 0, Padding = 0;
1623 RECT r = *lpRc;
1624 LONG ButtonWidth, IconWidth;
1625 BOOL HasIcon;
1626 PWINDOW Wnd = NULL;
1627
1628 //ASSERT(pWnd != NULL);
1629
1630 if (pWnd)
1631 Wnd = pWnd->Wnd;
1632
1633 hMemBmp = NtGdiCreateCompatibleBitmap(hDc,
1634 lpRc->right - lpRc->left,
1635 lpRc->bottom - lpRc->top);
1636
1637 if(!hMemBmp)
1638 {
1639 DPRINT1("%s: NtGdiCreateCompatibleBitmap() failed!\n", __FUNCTION__);
1640 return FALSE;
1641 }
1642
1643 hMemDc = NtGdiCreateCompatibleDC(hDc);
1644 if(!hMemDc)
1645 {
1646 DPRINT1("%s: NtGdiCreateCompatibleDC() failed!\n", __FUNCTION__);
1647 goto cleanup;
1648 }
1649
1650 hOldBmp = NtGdiSelectBitmap(hMemDc, hMemBmp);
1651 if(!hOldBmp)
1652 {
1653 DPRINT1("%s: NtGdiSelectBitmap() failed!\n", __FUNCTION__);
1654 goto cleanup;
1655 }
1656
1657 Height = UserGetSystemMetrics(SM_CYCAPTION) - 1;
1658 VCenter = (lpRc->bottom - lpRc->top) / 2;
1659 Padding = VCenter - (Height / 2);
1660
1661 if ((!hIcon) && (Wnd != NULL))
1662 {
1663 HasIcon = (uFlags & DC_ICON) && (Wnd->Style & WS_SYSMENU)
1664 && !(uFlags & DC_SMALLCAP) && !(Wnd->ExStyle & WS_EX_DLGMODALFRAME)
1665 && !(Wnd->ExStyle & WS_EX_TOOLWINDOW);
1666 }
1667 else
1668 HasIcon = (hIcon != 0);
1669
1670 IconWidth = UserGetSystemMetrics(SM_CXSIZE) + Padding;
1671
1672 r.left = Padding;
1673 r.right = r.left + (lpRc->right - lpRc->left);
1674 r.top = Padding;
1675 r.bottom = r.top + (Height / 2);
1676
1677 // Draw the caption background
1678 if(uFlags & DC_INBUTTON)
1679 {
1680 hOldBrush = NtGdiSelectBrush(hMemDc,
1681 IntGetSysColorBrush(uFlags & DC_ACTIVE ?
1682 COLOR_BTNFACE : COLOR_BTNSHADOW));
1683
1684 if(!hOldBrush)
1685 {
1686 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1687 goto cleanup;
1688 }
1689
1690 if(!NtGdiPatBlt(hMemDc, 0, 0,
1691 lpRc->right - lpRc->left,
1692 lpRc->bottom - lpRc->top,
1693 PATCOPY))
1694 {
1695 DPRINT1("%s: NtGdiPatBlt() failed!\n", __FUNCTION__);
1696 goto cleanup;
1697 }
1698
1699 if(HasIcon) r.left+=IconWidth;
1700 }
1701 else
1702 {
1703 r.right = (lpRc->right - lpRc->left);
1704 if(uFlags & DC_SMALLCAP)
1705 ButtonWidth = UserGetSystemMetrics(SM_CXSMSIZE) - 2;
1706 else ButtonWidth = UserGetSystemMetrics(SM_CXSIZE) - 2;
1707
1708 hOldBrush = NtGdiSelectBrush(hMemDc,
1709 IntGetSysColorBrush(uFlags & DC_ACTIVE ?
1710 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION));
1711
1712 if(!hOldBrush)
1713 {
1714 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1715 goto cleanup;
1716 }
1717
1718 if(HasIcon && (uFlags & DC_GRADIENT))
1719 {
1720 NtGdiPatBlt(hMemDc, 0, 0,
1721 IconWidth+1,
1722 lpRc->bottom - lpRc->top,
1723 PATCOPY);
1724 r.left+=IconWidth;
1725 }
1726 else
1727 {
1728 NtGdiPatBlt(hMemDc, 0, 0,
1729 lpRc->right - lpRc->left,
1730 lpRc->bottom - lpRc->top,
1731 PATCOPY);
1732 }
1733
1734 if(uFlags & DC_GRADIENT)
1735 {
1736 static GRADIENT_RECT gcap = {0, 1};
1737 TRIVERTEX vert[2];
1738 COLORREF Colors[2];
1739 PDC pMemDc;
1740
1741 if (Wnd != NULL)
1742 {
1743 if(Wnd->Style & WS_SYSMENU)
1744 {
1745 r.right -= 3 + ButtonWidth;
1746 if(!(uFlags & DC_SMALLCAP))
1747 {
1748 if(Wnd->Style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX))
1749 r.right -= 2 + 2 * ButtonWidth;
1750 else r.right -= 2;
1751 r.right -= 2;
1752 }
1753
1754 //Draw buttons background
1755 if(!NtGdiSelectBrush(hMemDc,
1756 IntGetSysColorBrush(uFlags & DC_ACTIVE ?
1757 COLOR_GRADIENTACTIVECAPTION:COLOR_GRADIENTINACTIVECAPTION)))
1758 {
1759 DPRINT1("%s: NtGdiSelectBrush() failed!\n", __FUNCTION__);
1760 goto cleanup;
1761 }
1762
1763 NtGdiPatBlt(hMemDc,
1764 r.right,
1765 0,
1766 lpRc->right - lpRc->left - r.right,
1767 lpRc->bottom - lpRc->top,
1768 PATCOPY);
1769 }
1770 }
1771
1772 Colors[0] = IntGetSysColor((uFlags & DC_ACTIVE) ?
1773 COLOR_ACTIVECAPTION : COLOR_INACTIVECAPTION);
1774
1775 Colors[1] = IntGetSysColor((uFlags & DC_ACTIVE) ?
1776 COLOR_GRADIENTACTIVECAPTION : COLOR_GRADIENTINACTIVECAPTION);
1777
1778 vert[0].x = r.left;
1779 vert[0].y = 0;
1780 vert[0].Red = (WORD)Colors[0]<<8;
1781 vert[0].Green = (WORD)Colors[0] & 0xFF00;
1782 vert[0].Blue = (WORD)(Colors[0]>>8) & 0xFF00;
1783 vert[0].Alpha = 0;
1784
1785 vert[1].x = r.right;
1786 vert[1].y = lpRc->bottom - lpRc->top;
1787 vert[1].Red = (WORD)Colors[1]<<8;
1788 vert[1].Green = (WORD)Colors[1] & 0xFF00;
1789 vert[1].Blue = (WORD)(Colors[1]>>8) & 0xFF00;
1790 vert[1].Alpha = 0;
1791
1792 pMemDc = DC_LockDc(hMemDc);
1793 if(!pMemDc)
1794 {
1795 DPRINT1("%s: Can't lock dc!\n", __FUNCTION__);
1796 goto cleanup;
1797 }
1798
1799 if(!IntGdiGradientFill(pMemDc, vert, 2, &gcap,
1800 1, GRADIENT_FILL_RECT_H))
1801 {
1802 DPRINT1("%s: IntGdiGradientFill() failed!\n", __FUNCTION__);
1803 }
1804
1805 DC_UnlockDc(pMemDc);
1806 } //if(uFlags & DC_GRADIENT)
1807 }
1808
1809 if(HasIcon)
1810 {
1811 r.top ++;
1812 r.left -= --IconWidth;
1813 /* FIXME: Draw the Icon when pWnd == NULL but hIcon is valid */
1814 if (pWnd != NULL)
1815 UserDrawSysMenuButton(pWnd, hMemDc, &r, FALSE);
1816 r.left += IconWidth;
1817 r.top --;
1818 }
1819
1820 r.top ++;
1821 r.left += 2;
1822
1823 r.bottom = r.top + Height;
1824
1825 if((uFlags & DC_TEXT))
1826 {
1827 if(!(uFlags & DC_GRADIENT))
1828 {
1829 r.right = (lpRc->right - lpRc->left);
1830
1831 if(uFlags & DC_SMALLCAP)
1832 ButtonWidth = UserGetSystemMetrics(SM_CXSMSIZE) - 2;
1833 else ButtonWidth = UserGetSystemMetrics(SM_CXSIZE) - 2;
1834
1835 if ((Wnd != NULL) && (Wnd->Style & WS_SYSMENU))
1836 {
1837 r.right -= 3 + ButtonWidth;
1838 if(! (uFlags & DC_SMALLCAP))
1839 {
1840 if(Wnd->Style & (WS_MAXIMIZEBOX | WS_MINIMIZEBOX))
1841 r.right -= 2 + 2 * ButtonWidth;
1842 else r.right -= 2;
1843 r.right -= 2;
1844 }
1845 }
1846 }
1847
1848 /* FIXME: hFont isn't handled */
1849 if (str)
1850 UserDrawCaptionText(hMemDc, str, &r, uFlags);
1851 else if (pWnd != NULL)
1852 UserDrawCaptionText(hMemDc, &pWnd->Wnd->WindowName, &r, uFlags);
1853 }
1854
1855 if(!NtGdiBitBlt(hDc, lpRc->left, lpRc->top,
1856 lpRc->right - lpRc->left, lpRc->bottom - lpRc->top,
1857 hMemDc, 0, 0, SRCCOPY, 0, 0))
1858 {
1859 DPRINT1("%s: NtGdiBitBlt() failed!\n", __FUNCTION__);
1860 goto cleanup;
1861 }
1862
1863 Ret = TRUE;
1864
1865 cleanup:
1866 if (hOldBrush) NtGdiSelectBrush(hMemDc, hOldBrush);
1867 if (hOldBmp) NtGdiSelectBitmap(hMemDc, hOldBmp);
1868 if (hMemBmp) NtGdiDeleteObject(hMemBmp);
1869 if (hMemDc) NtGdiDeleteObjectApp(hMemDc);
1870
1871 return Ret;
1872 }
1873
1874 INT
1875 FASTCALL
1876 UserRealizePalette(HDC hdc)
1877 {
1878 HWND hWnd;
1879 DWORD Ret;
1880
1881 Ret = IntGdiRealizePalette(hdc);
1882 if (Ret) // There was a change.
1883 {
1884 hWnd = IntWindowFromDC(hdc);
1885 if (hWnd) // Send broadcast if dc is associated with a window.
1886 { // FYI: Thread locked in CallOneParam.
1887 co_IntSendMessage((HWND)HWND_BROADCAST, WM_PALETTECHANGED, (WPARAM)hWnd, 0);
1888 }
1889 }
1890 return Ret;
1891 }
1892
1893 BOOL
1894 STDCALL
1895 NtUserDrawCaptionTemp(
1896 HWND hWnd,
1897 HDC hDC,
1898 LPCRECT lpRc,
1899 HFONT hFont,
1900 HICON hIcon,
1901 const PUNICODE_STRING str,
1902 UINT uFlags)
1903 {
1904 PWINDOW_OBJECT pWnd = NULL;
1905 RECT SafeRect;
1906 UNICODE_STRING SafeStr = {0};
1907 BOOL Ret = FALSE;
1908
1909 UserEnterExclusive();
1910
1911 if (hWnd != NULL)
1912 {
1913 if(!(pWnd = UserGetWindowObject(hWnd)))
1914 {
1915 UserLeave();
1916 return FALSE;
1917 }
1918 }
1919
1920 _SEH_TRY
1921 {
1922 ProbeForRead(lpRc, sizeof(RECT), sizeof(ULONG));
1923 RtlCopyMemory(&SafeRect, lpRc, sizeof(RECT));
1924 if (str != NULL)
1925 {
1926 SafeStr = ProbeForReadUnicodeString(str);
1927 if (SafeStr.Length != 0)
1928 {
1929 ProbeForRead(SafeStr.Buffer,
1930 SafeStr.Length,
1931 sizeof(WCHAR));
1932 }
1933 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, &SafeStr, uFlags);
1934 }
1935 else
1936 Ret = UserDrawCaption(pWnd, hDC, &SafeRect, hFont, hIcon, NULL, uFlags);
1937 }
1938 _SEH_HANDLE
1939 {
1940 SetLastNtError(_SEH_GetExceptionCode());
1941 }
1942 _SEH_END;
1943
1944 UserLeave();
1945 return Ret;
1946 }
1947
1948 BOOL
1949 STDCALL
1950 NtUserDrawCaption(HWND hWnd,
1951 HDC hDC,
1952 LPCRECT lpRc,
1953 UINT uFlags)
1954 {
1955 return NtUserDrawCaptionTemp(hWnd, hDC, lpRc, 0, 0, NULL, uFlags);
1956 }
1957
1958 BOOL
1959 NTAPI
1960 NtUserInvalidateRect(
1961 HWND hWnd,
1962 CONST RECT *lpUnsafeRect,
1963 BOOL bErase)
1964 {
1965 return NtUserRedrawWindow(hWnd, lpUnsafeRect, NULL, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
1966 }
1967
1968 BOOL
1969 NTAPI
1970 NtUserInvalidateRgn(
1971 HWND hWnd,
1972 HRGN hRgn,
1973 BOOL bErase)
1974 {
1975 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_INVALIDATE | (bErase? RDW_ERASE : 0));
1976 }
1977
1978 /* EOF */