-include/ndk/rtltypes.h: add MAX_ATOM_LEN define
[reactos.git] / reactos / subsys / 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
62 ParentWindow = Child->Parent;
63 while (ParentWindow != NULL)
64 {
65 if (!(ParentWindow->Style & WS_VISIBLE) ||
66 (ParentWindow->Style & WS_MINIMIZE))
67 {
68 return FALSE;
69 }
70
71 if (!IntGdiIntersectRect(WindowRect, WindowRect, &ParentWindow->ClientRect))
72 {
73 return FALSE;
74 }
75
76 /* FIXME: Layered windows. */
77
78 ParentWindow = ParentWindow->Parent;
79 }
80
81 return TRUE;
82 }
83
84 VOID FASTCALL
85 IntValidateParent(PWINDOW_OBJECT Child, HRGN ValidRegion)
86 {
87 PWINDOW_OBJECT ParentWindow = Child->Parent;
88
89 while (ParentWindow)
90 {
91 if (ParentWindow->Style & WS_CLIPCHILDREN)
92 break;
93
94 if (ParentWindow->UpdateRegion != 0)
95 {
96 NtGdiCombineRgn(ParentWindow->UpdateRegion, ParentWindow->UpdateRegion,
97 ValidRegion, RGN_DIFF);
98 /* FIXME: If the resulting region is empty, remove fake posted paint message */
99 }
100
101 ParentWindow = ParentWindow->Parent;
102 }
103 }
104
105 /**
106 * @name IntCalcWindowRgn
107 *
108 * Get a window or client region.
109 */
110
111 HRGN FASTCALL
112 IntCalcWindowRgn(PWINDOW_OBJECT Window, BOOL Client)
113 {
114 HRGN hRgnWindow;
115 UINT RgnType;
116
117 if (Client)
118 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
119 else
120 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
121
122 if (Window->WindowRegion != NULL && !(Window->Style & WS_MINIMIZE))
123 {
124 NtGdiOffsetRgn(hRgnWindow,
125 -Window->WindowRect.left,
126 -Window->WindowRect.top);
127 RgnType = NtGdiCombineRgn(hRgnWindow, hRgnWindow, Window->WindowRegion, RGN_AND);
128 NtGdiOffsetRgn(hRgnWindow,
129 Window->WindowRect.left,
130 Window->WindowRect.top);
131 }
132
133 return hRgnWindow;
134 }
135
136 /**
137 * @name IntGetNCUpdateRgn
138 *
139 * Get non-client update region of a window and optionally validate it.
140 *
141 * @param Window
142 * Pointer to window to get the NC update region from.
143 * @param Validate
144 * Set to TRUE to force validating the NC update region.
145 *
146 * @return
147 * Handle to NC update region. The caller is responsible for deleting
148 * it.
149 */
150
151 HRGN FASTCALL
152 IntGetNCUpdateRgn(PWINDOW_OBJECT Window, BOOL Validate)
153 {
154 HRGN hRgnNonClient;
155 HRGN hRgnWindow;
156 UINT RgnType;
157
158 if (Window->UpdateRegion != NULL &&
159 Window->UpdateRegion != (HRGN)1)
160 {
161 hRgnNonClient = NtGdiCreateRectRgn(0, 0, 0, 0);
162 hRgnWindow = IntCalcWindowRgn(Window, TRUE);
163
164 /*
165 * If region creation fails it's safe to fallback to whole
166 * window region.
167 */
168
169 if (hRgnNonClient == NULL)
170 {
171 return (HRGN)1;
172 }
173
174 RgnType = NtGdiCombineRgn(hRgnNonClient, Window->UpdateRegion,
175 hRgnWindow, RGN_DIFF);
176 if (RgnType == ERROR)
177 {
178 NtGdiDeleteObject(hRgnNonClient);
179 return (HRGN)1;
180 }
181 else if (RgnType == NULLREGION)
182 {
183 NtGdiDeleteObject(hRgnNonClient);
184 return NULL;
185 }
186
187 /*
188 * Remove the nonclient region from the standard update region if
189 * we were asked for it.
190 */
191
192 if (Validate)
193 {
194 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
195 hRgnWindow, RGN_AND) == NULLREGION)
196 {
197 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
198 NtGdiDeleteObject(Window->UpdateRegion);
199 Window->UpdateRegion = NULL;
200 if (!(Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT))
201 MsqDecPaintCountQueue(Window->MessageQueue);
202 }
203 }
204
205 NtGdiDeleteObject(hRgnWindow);
206
207 return hRgnNonClient;
208 }
209 else
210 {
211 return Window->UpdateRegion;
212 }
213 }
214
215 /*
216 * IntPaintWindows
217 *
218 * Internal function used by IntRedrawWindow.
219 */
220
221 STATIC VOID FASTCALL
222 co_IntPaintWindows(PWINDOW_OBJECT Window, ULONG Flags)
223 {
224 HDC hDC;
225 HWND hWnd = Window->hSelf;
226 HRGN TempRegion;
227
228 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
229 {
230 if (Window->UpdateRegion)
231 {
232 IntValidateParent(Window, Window->UpdateRegion);
233 }
234
235 if (Flags & RDW_UPDATENOW)
236 {
237 if (Window->UpdateRegion != NULL ||
238 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
239 {
240 co_IntSendMessage(hWnd, WM_PAINT, 0, 0);
241 }
242 }
243 else
244 {
245 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
246 {
247 TempRegion = IntGetNCUpdateRgn(Window, TRUE);
248 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
249 MsqDecPaintCountQueue(Window->MessageQueue);
250 co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)TempRegion, 0);
251 if ((HANDLE) 1 != TempRegion && NULL != TempRegion)
252 {
253 /* NOTE: The region can already be deleted! */
254 GDIOBJ_FreeObj(TempRegion, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
255 }
256 }
257
258 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
259 {
260 if (Window->UpdateRegion)
261 {
262 hDC = UserGetDCEx(Window, Window->UpdateRegion,
263 DCX_CACHE | DCX_USESTYLE |
264 DCX_INTERSECTRGN | DCX_KEEPCLIPRGN);
265 if (co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
266 {
267 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
268 }
269 UserReleaseDC(Window, hDC, FALSE);
270 }
271 }
272 }
273 }
274
275 /*
276 * Check that the window is still valid at this point
277 */
278 if (!IntIsWindow(hWnd))
279 {
280 return;
281 }
282
283 /*
284 * Paint child windows.
285 */
286 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
287 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
288 {
289 HWND *List, *phWnd;
290
291 if ((List = IntWinListChildren(Window)))
292 {
293 for (phWnd = List; *phWnd; ++phWnd)
294 {
295 Window = UserGetWindowObject(*phWnd);
296 if (Window && (Window->Style & WS_VISIBLE))
297 {
298 UserRefObjectCo(Window);
299 co_IntPaintWindows(Window, Flags);
300 UserDerefObjectCo(Window);
301 }
302 }
303 ExFreePool(List);
304 }
305 }
306 }
307
308 /*
309 * IntInvalidateWindows
310 *
311 * Internal function used by IntRedrawWindow.
312 */
313
314 VOID FASTCALL
315 IntInvalidateWindows(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags)
316 {
317 INT RgnType;
318 BOOL HadPaintMessage, HadNCPaintMessage;
319 BOOL HasPaintMessage, HasNCPaintMessage;
320
321 /*
322 * Clip the given region with window rectangle (or region)
323 */
324
325 if (!Window->WindowRegion || (Window->Style & WS_MINIMIZE))
326 {
327 HRGN hRgnWindow;
328
329 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
330 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnWindow, RGN_AND);
331 NtGdiDeleteObject(hRgnWindow);
332 }
333 else
334 {
335 NtGdiOffsetRgn(hRgn,
336 -Window->WindowRect.left,
337 -Window->WindowRect.top);
338 RgnType = NtGdiCombineRgn(hRgn, hRgn, Window->WindowRegion, RGN_AND);
339 NtGdiOffsetRgn(hRgn,
340 Window->WindowRect.left,
341 Window->WindowRect.top);
342 }
343
344 /*
345 * Save current state of pending updates
346 */
347
348 HadPaintMessage = Window->UpdateRegion != NULL ||
349 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
350 HadNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
351
352 /*
353 * Update the region and flags
354 */
355
356 if (Flags & RDW_INVALIDATE && RgnType != NULLREGION)
357 {
358 if (Window->UpdateRegion == NULL)
359 {
360 Window->UpdateRegion = NtGdiCreateRectRgn(0, 0, 0, 0);
361 GDIOBJ_SetOwnership(Window->UpdateRegion, NULL);
362 }
363
364 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
365 hRgn, RGN_OR) == NULLREGION)
366 {
367 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
368 NtGdiDeleteObject(Window->UpdateRegion);
369 Window->UpdateRegion = NULL;
370 }
371
372 if (Flags & RDW_FRAME)
373 Window->Flags |= WINDOWOBJECT_NEED_NCPAINT;
374 if (Flags & RDW_ERASE)
375 Window->Flags |= WINDOWOBJECT_NEED_ERASEBKGND;
376
377 Flags |= RDW_FRAME;
378 }
379
380 if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
381 {
382 if (Window->UpdateRegion != NULL)
383 {
384 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
385 hRgn, RGN_DIFF) == NULLREGION)
386 {
387 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
388 NtGdiDeleteObject(Window->UpdateRegion);
389 Window->UpdateRegion = NULL;
390 }
391 }
392
393 if (Window->UpdateRegion == NULL)
394 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
395 if (Flags & RDW_NOFRAME)
396 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
397 if (Flags & RDW_NOERASE)
398 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
399 }
400
401 if (Flags & RDW_INTERNALPAINT)
402 {
403 Window->Flags |= WINDOWOBJECT_NEED_INTERNALPAINT;
404 }
405
406 if (Flags & RDW_NOINTERNALPAINT)
407 {
408 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
409 }
410
411 /*
412 * Process children if needed
413 */
414
415 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
416 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
417 {
418 PWINDOW_OBJECT Child;
419
420 for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
421 {
422 if (Child->Style & WS_VISIBLE)
423 {
424 /*
425 * Recursive call to update children UpdateRegion
426 */
427 HRGN hRgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
428 NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
429 IntInvalidateWindows(Child, hRgnTemp, Flags);
430 NtGdiDeleteObject(hRgnTemp);
431 }
432
433 }
434 }
435
436 /*
437 * Fake post paint messages to window message queue if needed
438 */
439
440 HasPaintMessage = Window->UpdateRegion != NULL ||
441 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
442 HasNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
443
444 if (HasPaintMessage != HadPaintMessage)
445 {
446 if (HadPaintMessage)
447 MsqDecPaintCountQueue(Window->MessageQueue);
448 else
449 MsqIncPaintCountQueue(Window->MessageQueue);
450 }
451
452 if (HasNCPaintMessage != HadNCPaintMessage)
453 {
454 if (HadNCPaintMessage)
455 MsqDecPaintCountQueue(Window->MessageQueue);
456 else
457 MsqIncPaintCountQueue(Window->MessageQueue);
458 }
459
460 }
461
462 /*
463 * IntIsWindowDrawable
464 *
465 * Remarks
466 * Window is drawable when it is visible and all parents are not
467 * minimized.
468 */
469
470 BOOL FASTCALL
471 IntIsWindowDrawable(PWINDOW_OBJECT Window)
472 {
473 PWINDOW_OBJECT Wnd;
474
475 for (Wnd = Window; Wnd != NULL; Wnd = Wnd->Parent)
476 {
477 if (!(Wnd->Style & WS_VISIBLE) ||
478 ((Wnd->Style & WS_MINIMIZE) && (Wnd != Window)))
479 {
480 return FALSE;
481 }
482 }
483
484 return TRUE;
485 }
486
487 /*
488 * IntRedrawWindow
489 *
490 * Internal version of NtUserRedrawWindow that takes WINDOW_OBJECT as
491 * first parameter.
492 */
493
494 BOOL FASTCALL
495 co_UserRedrawWindow(PWINDOW_OBJECT Window, const RECT* UpdateRect, HRGN UpdateRgn,
496 ULONG Flags)
497 {
498 HRGN hRgn = NULL;
499
500 /*
501 * Step 1.
502 * Validation of passed parameters.
503 */
504
505 if (!IntIsWindowDrawable(Window) ||
506 (Flags & (RDW_VALIDATE | RDW_INVALIDATE)) ==
507 (RDW_VALIDATE | RDW_INVALIDATE))
508 {
509 return FALSE;
510 }
511
512 /*
513 * Step 2.
514 * Transform the parameters UpdateRgn and UpdateRect into
515 * a region hRgn specified in screen coordinates.
516 */
517
518 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE))
519 {
520 if (UpdateRgn != NULL)
521 {
522 hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
523 if (NtGdiCombineRgn(hRgn, UpdateRgn, NULL, RGN_COPY) == NULLREGION)
524 NtGdiDeleteObject(hRgn);
525 else
526 NtGdiOffsetRgn(hRgn, Window->ClientRect.left, Window->ClientRect.top);
527 }
528 else if (UpdateRect != NULL)
529 {
530 if (!IntGdiIsEmptyRect(UpdateRect))
531 {
532 hRgn = UnsafeIntCreateRectRgnIndirect((RECT *)UpdateRect);
533 NtGdiOffsetRgn(hRgn, Window->ClientRect.left, Window->ClientRect.top);
534 }
535 }
536 else if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
537 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
538 {
539 if (!IntGdiIsEmptyRect(&Window->WindowRect))
540 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
541 }
542 else
543 {
544 if (!IntGdiIsEmptyRect(&Window->ClientRect))
545 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
546 }
547 }
548
549 /*
550 * Step 3.
551 * Adjust the window update region depending on hRgn and flags.
552 */
553
554 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT) &&
555 hRgn != NULL)
556 {
557 IntInvalidateWindows(Window, hRgn, Flags);
558 }
559
560 /*
561 * Step 4.
562 * Repaint and erase windows if needed.
563 */
564
565 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
566 {
567 co_IntPaintWindows(Window, Flags);
568 }
569
570 /*
571 * Step 5.
572 * Cleanup ;-)
573 */
574
575 if (hRgn != NULL)
576 {
577 NtGdiDeleteObject(hRgn);
578 }
579
580 return TRUE;
581 }
582
583 BOOL FASTCALL
584 IntIsWindowDirty(PWINDOW_OBJECT Window)
585 {
586 return (Window->Style & WS_VISIBLE) &&
587 ((Window->UpdateRegion != NULL) ||
588 (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT) ||
589 (Window->Flags & WINDOWOBJECT_NEED_NCPAINT));
590 }
591
592 HWND FASTCALL
593 IntFindWindowToRepaint(PWINDOW_OBJECT Window, PW32THREAD Thread)
594 {
595 HWND hChild;
596 PWINDOW_OBJECT TempWindow;
597
598 for (; Window != NULL; Window = Window->NextSibling)
599 {
600 if (IntWndBelongsToThread(Window, Thread) &&
601 IntIsWindowDirty(Window))
602 {
603 /* Make sure all non-transparent siblings are already drawn. */
604 if (Window->ExStyle & WS_EX_TRANSPARENT)
605 {
606 for (TempWindow = Window->NextSibling; TempWindow != NULL;
607 TempWindow = TempWindow->NextSibling)
608 {
609 if (!(TempWindow->ExStyle & WS_EX_TRANSPARENT) &&
610 IntWndBelongsToThread(TempWindow, Thread) &&
611 IntIsWindowDirty(TempWindow))
612 {
613 return TempWindow->hSelf;
614 }
615 }
616 }
617
618 return Window->hSelf;
619 }
620
621 if (Window->FirstChild)
622 {
623 hChild = IntFindWindowToRepaint(Window->FirstChild, Thread);
624 if (hChild != NULL)
625 return hChild;
626 }
627 }
628
629 return NULL;
630 }
631
632 BOOL FASTCALL
633 IntGetPaintMessage(HWND hWnd, UINT MsgFilterMin, UINT MsgFilterMax,
634 PW32THREAD Thread, MSG *Message, BOOL Remove)
635 {
636 PUSER_MESSAGE_QUEUE MessageQueue = (PUSER_MESSAGE_QUEUE)Thread->MessageQueue;
637
638 if (!MessageQueue->PaintCount)
639 return FALSE;
640
641 if ((MsgFilterMin != 0 || MsgFilterMax != 0) &&
642 (MsgFilterMin > WM_PAINT || MsgFilterMax < WM_PAINT))
643 return FALSE;
644
645 Message->hwnd = IntFindWindowToRepaint(UserGetDesktopWindow(), PsGetWin32Thread());
646
647 if (Message->hwnd == NULL)
648 {
649 DPRINT1("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found!\n");
650 return FALSE;
651 }
652
653 if (hWnd != NULL && Message->hwnd != hWnd)
654 return FALSE;
655
656 Message->message = WM_PAINT;
657 Message->wParam = Message->lParam = 0;
658
659 return TRUE;
660 }
661
662 static
663 HWND FASTCALL
664 co_IntFixCaret(PWINDOW_OBJECT Window, LPRECT lprc, UINT flags)
665 {
666 PDESKTOP_OBJECT Desktop;
667 PTHRDCARETINFO CaretInfo;
668 HWND hWndCaret;
669 PWINDOW_OBJECT WndCaret;
670
671 ASSERT_REFS_CO(Window);
672
673 Desktop = PsGetCurrentThread()->Tcb.Win32Thread->Desktop;
674 CaretInfo = ((PUSER_MESSAGE_QUEUE)Desktop->ActiveMessageQueue)->CaretInfo;
675 hWndCaret = CaretInfo->hWnd;
676
677 WndCaret = UserGetWindowObject(hWndCaret);
678
679 //fix: check for WndCaret can be null
680 if (WndCaret == Window ||
681 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(Window, WndCaret)))
682 {
683 POINT pt, FromOffset, ToOffset, Offset;
684 RECT rcCaret;
685
686 pt.x = CaretInfo->Pos.x;
687 pt.y = CaretInfo->Pos.y;
688 IntGetClientOrigin(WndCaret, &FromOffset);
689 IntGetClientOrigin(Window, &ToOffset);
690 Offset.x = FromOffset.x - ToOffset.x;
691 Offset.y = FromOffset.y - ToOffset.y;
692 rcCaret.left = pt.x;
693 rcCaret.top = pt.y;
694 rcCaret.right = pt.x + CaretInfo->Size.cx;
695 rcCaret.bottom = pt.y + CaretInfo->Size.cy;
696 if (IntGdiIntersectRect(lprc, lprc, &rcCaret))
697 {
698 co_UserHideCaret(0);
699 lprc->left = pt.x;
700 lprc->top = pt.y;
701 return hWndCaret;
702 }
703 }
704
705 return 0;
706 }
707
708 /* PUBLIC FUNCTIONS ***********************************************************/
709
710 /*
711 * NtUserBeginPaint
712 *
713 * Status
714 * @implemented
715 */
716
717 HDC STDCALL
718 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* UnsafePs)
719 {
720 PWINDOW_OBJECT Window = NULL;
721 PAINTSTRUCT Ps;
722 PROSRGNDATA Rgn;
723 NTSTATUS Status;
724 DECLARE_RETURN(HDC);
725
726 DPRINT("Enter NtUserBeginPaint\n");
727 UserEnterExclusive();
728
729 if (!(Window = UserGetWindowObject(hWnd)))
730 {
731 RETURN( NULL);
732 }
733
734 UserRefObjectCo(Window);
735
736 co_UserHideCaret(Window);
737
738 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
739 {
740 HRGN hRgn;
741
742 hRgn = IntGetNCUpdateRgn(Window, FALSE);
743 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
744 MsqDecPaintCountQueue(Window->MessageQueue);
745 co_IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)hRgn, 0);
746 if (hRgn != (HANDLE)1 && hRgn != NULL)
747 {
748 /* NOTE: The region can already by deleted! */
749 GDIOBJ_FreeObj(hRgn, GDI_OBJECT_TYPE_REGION | GDI_OBJECT_TYPE_SILENT);
750 }
751 }
752
753 RtlZeroMemory(&Ps, sizeof(PAINTSTRUCT));
754
755 Ps.hdc = UserGetDCEx(Window, Window->UpdateRegion, DCX_INTERSECTRGN | DCX_USESTYLE);
756 if (!Ps.hdc)
757 {
758 RETURN( NULL);
759 }
760
761 if (Window->UpdateRegion != NULL)
762 {
763 MsqDecPaintCountQueue(Window->MessageQueue);
764 Rgn = RGNDATA_LockRgn(Window->UpdateRegion);
765 if (NULL != Rgn)
766 {
767 UnsafeIntGetRgnBox(Rgn, &Ps.rcPaint);
768 RGNDATA_UnlockRgn(Rgn);
769 IntGdiOffsetRect(&Ps.rcPaint,
770 -Window->ClientRect.left,
771 -Window->ClientRect.top);
772 }
773 else
774 {
775 IntGetClientRect(Window, &Ps.rcPaint);
776 }
777 GDIOBJ_SetOwnership(Window->UpdateRegion, PsGetCurrentProcess());
778 Window->UpdateRegion = NULL;
779 }
780 else
781 {
782 if (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
783 MsqDecPaintCountQueue(Window->MessageQueue);
784
785 IntGetClientRect(Window, &Ps.rcPaint);
786 }
787
788 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
789
790 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
791 {
792 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
793 Ps.fErase = !co_IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)Ps.hdc, 0);
794 }
795 else
796 {
797 Ps.fErase = FALSE;
798 }
799
800 Status = MmCopyToCaller(UnsafePs, &Ps, sizeof(PAINTSTRUCT));
801 if (! NT_SUCCESS(Status))
802 {
803 SetLastNtError(Status);
804 RETURN( NULL);
805 }
806
807 RETURN( Ps.hdc);
808
809 CLEANUP:
810 if (Window) UserDerefObjectCo(Window);
811
812 DPRINT("Leave NtUserBeginPaint, ret=%i\n",_ret_);
813 UserLeave();
814 END_CLEANUP;
815
816 }
817
818 /*
819 * NtUserEndPaint
820 *
821 * Status
822 * @implemented
823 */
824
825 BOOL STDCALL
826 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* lPs)
827 {
828 PWINDOW_OBJECT Window;
829 DECLARE_RETURN(BOOL);
830
831 DPRINT("Enter NtUserEndPaint\n");
832 UserEnterExclusive();
833
834 if (!(Window = UserGetWindowObject(hWnd)))
835 {
836 RETURN(FALSE);
837 }
838
839 UserReleaseDC(Window, lPs->hdc, TRUE);
840
841 UserRefObjectCo(Window);
842 co_UserShowCaret(Window);
843 UserDerefObjectCo(Window);
844
845 RETURN(TRUE);
846
847 CLEANUP:
848 DPRINT("Leave NtUserEndPaint, ret=%i\n",_ret_);
849 UserLeave();
850 END_CLEANUP;
851 }
852
853
854 INT FASTCALL
855 co_UserGetUpdateRgn(PWINDOW_OBJECT Window, HRGN hRgn, BOOL bErase)
856 {
857 int RegionType;
858
859 ASSERT_REFS_CO(Window);
860
861 if (Window->UpdateRegion == NULL)
862 {
863 RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
864 }
865 else
866 {
867 RegionType = NtGdiCombineRgn(hRgn, Window->UpdateRegion, hRgn, RGN_COPY);
868 NtGdiOffsetRgn(hRgn, -Window->ClientRect.left, -Window->ClientRect.top);
869 }
870
871 if (bErase && RegionType != NULLREGION && RegionType != ERROR)
872 {
873 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
874 }
875
876 return RegionType;
877 }
878 /*
879 * NtUserGetUpdateRgn
880 *
881 * Status
882 * @implemented
883 */
884
885 INT STDCALL
886 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
887 {
888 DECLARE_RETURN(INT);
889 PWINDOW_OBJECT Window;
890 INT ret;
891
892 DPRINT("Enter NtUserGetUpdateRgn\n");
893 UserEnterExclusive();
894
895 if (!(Window = UserGetWindowObject(hWnd)))
896 {
897 RETURN(ERROR);
898 }
899
900 UserRefObjectCo(Window);
901 ret = co_UserGetUpdateRgn(Window, hRgn, bErase);
902 UserDerefObjectCo(Window);
903
904 RETURN(ret);
905
906 CLEANUP:
907 DPRINT("Leave NtUserGetUpdateRgn, ret=%i\n",_ret_);
908 UserLeave();
909 END_CLEANUP;
910 }
911
912 /*
913 * NtUserGetUpdateRect
914 *
915 * Status
916 * @implemented
917 */
918
919 BOOL STDCALL
920 NtUserGetUpdateRect(HWND hWnd, LPRECT UnsafeRect, BOOL bErase)
921 {
922 PWINDOW_OBJECT Window;
923 RECT Rect;
924 INT RegionType;
925 PROSRGNDATA RgnData;
926 BOOL AlwaysPaint;
927 NTSTATUS Status;
928 DECLARE_RETURN(BOOL);
929
930 DPRINT("Enter NtUserGetUpdateRect\n");
931 UserEnterExclusive();
932
933 if (!(Window = UserGetWindowObject(hWnd)))
934 {
935 RETURN( ERROR);
936 }
937
938 if (Window->UpdateRegion == NULL)
939 {
940 Rect.left = Rect.top = Rect.right = Rect.bottom = 0;
941 }
942 else
943 {
944 RgnData = RGNDATA_LockRgn(Window->UpdateRegion);
945 ASSERT(RgnData != NULL);
946 RegionType = UnsafeIntGetRgnBox(RgnData, &Rect);
947 ASSERT(RegionType != ERROR);
948 RGNDATA_UnlockRgn(RgnData);
949 }
950 AlwaysPaint = (Window->Flags & WINDOWOBJECT_NEED_NCPAINT) ||
951 (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT);
952
953 if (bErase && Rect.left < Rect.right && Rect.top < Rect.bottom)
954 {
955 UserRefObjectCo(Window);
956 co_UserRedrawWindow(Window, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
957 UserDerefObjectCo(Window);
958 }
959
960 if (UnsafeRect != NULL)
961 {
962 Status = MmCopyToCaller(UnsafeRect, &Rect, sizeof(RECT));
963 if (!NT_SUCCESS(Status))
964 {
965 SetLastWin32Error(ERROR_INVALID_PARAMETER);
966 RETURN( FALSE);
967 }
968 }
969
970 RETURN( (Rect.left < Rect.right && Rect.top < Rect.bottom) || AlwaysPaint);
971
972 CLEANUP:
973 DPRINT("Leave NtUserGetUpdateRect, ret=%i\n",_ret_);
974 UserLeave();
975 END_CLEANUP;
976 }
977
978 /*
979 * NtUserRedrawWindow
980 *
981 * Status
982 * @implemented
983 */
984
985 BOOL STDCALL
986 NtUserRedrawWindow(HWND hWnd, CONST RECT *lprcUpdate, HRGN hrgnUpdate,
987 UINT flags)
988 {
989 RECT SafeUpdateRect;
990 NTSTATUS Status;
991 PWINDOW_OBJECT Wnd;
992 DECLARE_RETURN(BOOL);
993
994 DPRINT("Enter NtUserRedrawWindow\n");
995 UserEnterExclusive();
996
997 if (!(Wnd = UserGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
998 {
999 RETURN( FALSE);
1000 }
1001
1002 if (lprcUpdate != NULL)
1003 {
1004 Status = MmCopyFromCaller(&SafeUpdateRect, (PRECT)lprcUpdate,
1005 sizeof(RECT));
1006
1007 if (!NT_SUCCESS(Status))
1008 {
1009 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1010 RETURN( FALSE);
1011 }
1012 }
1013
1014 UserRefObjectCo(Wnd);
1015
1016 Status = co_UserRedrawWindow(Wnd, NULL == lprcUpdate ? NULL : &SafeUpdateRect,
1017 hrgnUpdate, flags);
1018
1019 UserDerefObjectCo(Wnd);
1020
1021 if (!NT_SUCCESS(Status))
1022 {
1023 /* IntRedrawWindow fails only in case that flags are invalid */
1024 SetLastWin32Error(ERROR_INVALID_PARAMETER);
1025 RETURN( FALSE);
1026 }
1027
1028 RETURN( TRUE);
1029
1030 CLEANUP:
1031 DPRINT("Leave NtUserRedrawWindow, ret=%i\n",_ret_);
1032 UserLeave();
1033 END_CLEANUP;
1034 }
1035
1036
1037
1038 static
1039 DWORD FASTCALL
1040 UserScrollDC(HDC hDC, INT dx, INT dy, const RECT *lprcScroll,
1041 const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
1042 {
1043 RECT rSrc, rClipped_src, rClip, rDst, offset;
1044 PDC DC;
1045
1046 /*
1047 * Compute device clipping region (in device coordinates).
1048 */
1049
1050 DC = DC_LockDc(hDC);
1051 if (NULL == DC)
1052 {
1053 return FALSE;
1054 }
1055 if (lprcScroll)
1056 rSrc = *lprcScroll;
1057 else
1058 IntGdiGetClipBox(hDC, &rSrc);
1059 IntLPtoDP(DC, (LPPOINT)&rSrc, 2);
1060
1061 if (lprcClip)
1062 rClip = *lprcClip;
1063 else
1064 IntGdiGetClipBox(hDC, &rClip);
1065 IntLPtoDP(DC, (LPPOINT)&rClip, 2);
1066
1067 IntGdiIntersectRect(&rClipped_src, &rSrc, &rClip);
1068
1069 rDst = rClipped_src;
1070 IntGdiSetRect(&offset, 0, 0, dx, dy);
1071 IntLPtoDP(DC, (LPPOINT)&offset, 2);
1072 IntGdiOffsetRect(&rDst, offset.right - offset.left, offset.bottom - offset.top);
1073 IntGdiIntersectRect(&rDst, &rDst, &rClip);
1074
1075 /*
1076 * Copy bits, if possible.
1077 */
1078
1079 if (rDst.bottom > rDst.top && rDst.right > rDst.left)
1080 {
1081 RECT rDst_lp = rDst, rSrc_lp = rDst;
1082
1083 IntGdiOffsetRect(&rSrc_lp, offset.left - offset.right, offset.top - offset.bottom);
1084 IntDPtoLP(DC, (LPPOINT)&rDst_lp, 2);
1085 IntDPtoLP(DC, (LPPOINT)&rSrc_lp, 2);
1086 DC_UnlockDc(DC);
1087
1088 if (!NtGdiBitBlt(hDC, rDst_lp.left, rDst_lp.top, rDst_lp.right - rDst_lp.left,
1089 rDst_lp.bottom - rDst_lp.top, hDC, rSrc_lp.left, rSrc_lp.top,
1090 SRCCOPY))
1091 return FALSE;
1092 }
1093 else
1094 {
1095 DC_UnlockDc(DC);
1096 }
1097
1098 /*
1099 * Compute update areas. This is the clipped source or'ed with the
1100 * unclipped source translated minus the clipped src translated (rDst)
1101 * all clipped to rClip.
1102 */
1103
1104 if (hrgnUpdate || lprcUpdate)
1105 {
1106 HRGN hRgn = hrgnUpdate, hRgn2;
1107
1108 if (hRgn)
1109 NtGdiSetRectRgn(hRgn, rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1110 else
1111 hRgn = NtGdiCreateRectRgn(rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1112
1113 hRgn2 = UnsafeIntCreateRectRgnIndirect(&rSrc);
1114 NtGdiOffsetRgn(hRgn2, offset.right - offset.left, offset.bottom - offset.top);
1115 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_OR);
1116
1117 NtGdiSetRectRgn(hRgn2, rDst.left, rDst.top, rDst.right, rDst.bottom);
1118 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_DIFF);
1119
1120 NtGdiSetRectRgn(hRgn2, rClip.left, rClip.top, rClip.right, rClip.bottom);
1121 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_AND);
1122
1123 if (lprcUpdate)
1124 {
1125 NtGdiGetRgnBox(hRgn, lprcUpdate);
1126
1127 /* Put the lprcUpdate in logical coordinate */
1128 NtGdiDPtoLP(hDC, (LPPOINT)lprcUpdate, 2);
1129 }
1130 if (!hrgnUpdate)
1131 NtGdiDeleteObject(hRgn);
1132 NtGdiDeleteObject(hRgn2);
1133 }
1134 return TRUE;
1135 }
1136
1137
1138
1139
1140 /*
1141 * NtUserScrollDC
1142 *
1143 * Status
1144 * @implemented
1145 */
1146
1147 DWORD STDCALL
1148 NtUserScrollDC(HDC hDC, INT dx, INT dy, const RECT *lprcScroll,
1149 const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
1150 {
1151 DECLARE_RETURN(DWORD);
1152
1153 DPRINT("Enter NtUserScrollDC\n");
1154 UserEnterExclusive();
1155
1156 RETURN( UserScrollDC(hDC, dx, dy, lprcScroll, lprcClip, hrgnUpdate, lprcUpdate));
1157
1158 CLEANUP:
1159 DPRINT("Leave NtUserScrollDC, ret=%i\n",_ret_);
1160 UserLeave();
1161 END_CLEANUP;
1162
1163 }
1164
1165 /*
1166 * NtUserScrollWindowEx
1167 *
1168 * Status
1169 * @implemented
1170 */
1171
1172 DWORD STDCALL
1173 NtUserScrollWindowEx(HWND hWnd, INT dx, INT dy, const RECT *UnsafeRect,
1174 const RECT *UnsafeClipRect, HRGN hrgnUpdate, LPRECT rcUpdate, UINT flags)
1175 {
1176 RECT rc, cliprc, caretrc, rect, clipRect;
1177 INT Result;
1178 PWINDOW_OBJECT Window = NULL, CaretWnd;
1179 HDC hDC;
1180 HRGN hrgnTemp;
1181 HWND hwndCaret;
1182 BOOL bUpdate = (rcUpdate || hrgnUpdate || flags & (SW_INVALIDATE | SW_ERASE));
1183 BOOL bOwnRgn = TRUE;
1184 NTSTATUS Status;
1185 DECLARE_RETURN(DWORD);
1186
1187 DPRINT("Enter NtUserScrollWindowEx\n");
1188 UserEnterExclusive();
1189
1190 Window = UserGetWindowObject(hWnd);
1191 if (!Window || !IntIsWindowDrawable(Window))
1192 {
1193 Window = NULL; /* prevent deref at cleanup */
1194 RETURN( ERROR);
1195 }
1196 UserRefObjectCo(Window);
1197
1198 IntGetClientRect(Window, &rc);
1199
1200 if (NULL != UnsafeRect)
1201 {
1202 Status = MmCopyFromCaller(&rect, UnsafeRect, sizeof(RECT));
1203 if (! NT_SUCCESS(Status))
1204 {
1205 SetLastNtError(Status);
1206 RETURN( ERROR);
1207 }
1208 IntGdiIntersectRect(&rc, &rc, &rect);
1209 }
1210
1211 if (NULL != UnsafeClipRect)
1212 {
1213 Status = MmCopyFromCaller(&clipRect, UnsafeClipRect, sizeof(RECT));
1214 if (! NT_SUCCESS(Status))
1215 {
1216 SetLastNtError(Status);
1217 RETURN( ERROR);
1218 }
1219 IntGdiIntersectRect(&cliprc, &rc, &clipRect);
1220 }
1221 else
1222 cliprc = rc;
1223
1224 if (cliprc.right <= cliprc.left || cliprc.bottom <= cliprc.top ||
1225 (dx == 0 && dy == 0))
1226 {
1227 RETURN( NULLREGION);
1228 }
1229
1230 caretrc = rc;
1231 hwndCaret = co_IntFixCaret(Window, &caretrc, flags);
1232
1233 if (hrgnUpdate)
1234 bOwnRgn = FALSE;
1235 else if (bUpdate)
1236 hrgnUpdate = NtGdiCreateRectRgn(0, 0, 0, 0);
1237
1238 hDC = UserGetDCEx(Window, 0, DCX_CACHE | DCX_USESTYLE);
1239 if (hDC)
1240 {
1241 UserScrollDC(hDC, dx, dy, &rc, &cliprc, hrgnUpdate, rcUpdate);
1242 UserReleaseDC(Window, hDC, FALSE);
1243 }
1244
1245 /*
1246 * Take into account the fact that some damage may have occurred during
1247 * the scroll.
1248 */
1249
1250 hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
1251 Result = co_UserGetUpdateRgn(Window, hrgnTemp, FALSE);
1252 if (Result != NULLREGION)
1253 {
1254 HRGN hrgnClip = UnsafeIntCreateRectRgnIndirect(&cliprc);
1255 NtGdiOffsetRgn(hrgnTemp, dx, dy);
1256 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
1257 co_UserRedrawWindow(Window, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
1258 NtGdiDeleteObject(hrgnClip);
1259 }
1260
1261 NtGdiDeleteObject(hrgnTemp);
1262
1263 if (flags & SW_SCROLLCHILDREN)
1264 {
1265 HWND *List = IntWinListChildren(Window);
1266 if (List)
1267 {
1268 int i;
1269 RECT r, dummy;
1270 POINT ClientOrigin;
1271 PWINDOW_OBJECT Wnd;
1272
1273 IntGetClientOrigin(Window, &ClientOrigin);
1274 for (i = 0; List[i]; i++)
1275 {
1276 if (!(Wnd = UserGetWindowObject(List[i])))
1277 continue;
1278
1279 r = Wnd->WindowRect;
1280 r.left -= ClientOrigin.x;
1281 r.top -= ClientOrigin.y;
1282 r.right -= ClientOrigin.x;
1283 r.bottom -= ClientOrigin.y;
1284
1285 if (! UnsafeRect || IntGdiIntersectRect(&dummy, &r, &rc))
1286 {
1287 UserRefObjectCo(Wnd);
1288 co_WinPosSetWindowPos(Wnd, 0, r.left + dx, r.top + dy, 0, 0,
1289 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1290 SWP_NOREDRAW);
1291 UserDerefObjectCo(Wnd);
1292 }
1293
1294 }
1295 ExFreePool(List);
1296 }
1297 }
1298
1299 if (flags & (SW_INVALIDATE | SW_ERASE))
1300 co_UserRedrawWindow(Window, NULL, hrgnUpdate, RDW_INVALIDATE | RDW_ERASE |
1301 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
1302 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
1303
1304 if (bOwnRgn && hrgnUpdate)
1305 NtGdiDeleteObject(hrgnUpdate);
1306
1307 if ((CaretWnd = UserGetWindowObject(hwndCaret)))
1308 {
1309 UserRefObjectCo(CaretWnd);
1310
1311 co_IntSetCaretPos(caretrc.left + dx, caretrc.top + dy);
1312 co_UserShowCaret(CaretWnd);
1313
1314 UserDerefObjectCo(CaretWnd);
1315 }
1316
1317 RETURN( Result);
1318
1319 CLEANUP:
1320 if (Window)
1321 UserDerefObjectCo(Window);
1322
1323 DPRINT("Leave NtUserScrollWindowEx, ret=%i\n",_ret_);
1324 UserLeave();
1325 END_CLEANUP;
1326 }
1327
1328 /* EOF */