1. get rid of winlock.c
[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: painting.c,v 1.73 2004/02/24 01:30:57 weiden Exp $
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 <ddk/ntddk.h>
34 #include <internal/safe.h>
35 #include <win32k/win32k.h>
36 #include <include/object.h>
37 #include <include/guicheck.h>
38 #include <include/window.h>
39 #include <include/desktop.h>
40 #include <include/winpos.h>
41 #include <include/class.h>
42 #include <include/caret.h>
43 #include <include/error.h>
44 #include <include/winsta.h>
45 #include <windows.h>
46 #include <include/painting.h>
47 #include <user32/wininternal.h>
48 #include <include/rect.h>
49 #include <win32k/coord.h>
50 #include <win32k/region.h>
51 #include <include/vis.h>
52 #include <include/intgdi.h>
53
54 #define NDEBUG
55 #include <win32k/debug1.h>
56
57 /* PRIVATE FUNCTIONS **********************************************************/
58
59 VOID FASTCALL
60 IntValidateParent(PWINDOW_OBJECT Child, HRGN ValidRegion)
61 {
62 PWINDOW_OBJECT ParentWindow = IntGetParentObject(Child), OldWindow;
63
64 while (ParentWindow)
65 {
66 if (!(ParentWindow->Style & WS_CLIPCHILDREN))
67 {
68 ExAcquireFastMutex(&ParentWindow->UpdateLock);
69 if (ParentWindow->UpdateRegion != 0)
70 {
71 INT OffsetX, OffsetY;
72
73 /*
74 * We must offset the child region by the offset of the
75 * child rect in the parent.
76 */
77 OffsetX = Child->WindowRect.left - ParentWindow->WindowRect.left;
78 OffsetY = Child->WindowRect.top - ParentWindow->WindowRect.top;
79 NtGdiOffsetRgn(ValidRegion, OffsetX, OffsetY);
80 NtGdiCombineRgn(ParentWindow->UpdateRegion, ParentWindow->UpdateRegion,
81 ValidRegion, RGN_DIFF);
82 /* FIXME: If the resulting region is empty, remove fake posted paint message */
83 NtGdiOffsetRgn(ValidRegion, -OffsetX, -OffsetY);
84 }
85 ExReleaseFastMutex(&ParentWindow->UpdateLock);
86 }
87 OldWindow = ParentWindow;
88 ParentWindow = IntGetParentObject(ParentWindow);
89 IntReleaseWindowObject(OldWindow);
90 }
91 }
92
93 /*
94 * IntPaintWindows
95 *
96 * Internal function used by IntRedrawWindow.
97 */
98
99 STATIC VOID FASTCALL
100 IntPaintWindows(PWINDOW_OBJECT Window, ULONG Flags)
101 {
102 HDC hDC;
103 HWND hWnd = Window->Self;
104 HRGN TempRegion;
105
106 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
107 {
108 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
109 {
110 ExAcquireFastMutex(&Window->UpdateLock);
111 if (Window->NCUpdateRegion)
112 {
113 IntValidateParent(Window, Window->NCUpdateRegion);
114 }
115 TempRegion = Window->NCUpdateRegion;
116 Window->NCUpdateRegion = NULL;
117 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
118 MsqDecPaintCountQueue(Window->MessageQueue);
119 ExReleaseFastMutex(&Window->UpdateLock);
120 IntSendMessage(hWnd, WM_NCPAINT, (WPARAM)TempRegion, 0);
121 }
122
123 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
124 {
125 if (Window->UpdateRegion)
126 {
127 IntValidateParent(Window, Window->UpdateRegion);
128 hDC = NtUserGetDCEx(hWnd, 0, DCX_CACHE | DCX_USESTYLE |
129 DCX_INTERSECTUPDATE);
130 if (hDC != NULL)
131 {
132 if (IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)hDC, 0))
133 {
134 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
135 }
136 NtUserReleaseDC(hWnd, hDC);
137 }
138 }
139 }
140
141 if (Flags & RDW_UPDATENOW)
142 {
143 if (Window->UpdateRegion != NULL ||
144 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
145 {
146 IntSendMessage(hWnd, WM_PAINT, 0, 0);
147 ExAcquireFastMutex(&Window->UpdateLock);
148 if (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
149 {
150 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
151 if (Window->UpdateRegion == NULL)
152 {
153 MsqDecPaintCountQueue(Window->MessageQueue);
154 }
155 }
156 ExReleaseFastMutex(&Window->UpdateLock);
157 }
158 }
159 }
160
161 /*
162 * Check that the window is still valid at this point
163 */
164
165 if (! IntIsWindow(hWnd))
166 {
167 return;
168 }
169
170 /*
171 * Paint child windows.
172 */
173 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
174 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
175 {
176 HWND *List, *phWnd;
177
178 if ((List = IntWinListChildren(Window)))
179 {
180 for (phWnd = List; *phWnd; ++phWnd)
181 {
182 Window = IntGetWindowObject(*phWnd);
183 if (Window && (Window->Style & WS_VISIBLE))
184 {
185 IntPaintWindows(Window, Flags);
186 IntReleaseWindowObject(Window);
187 }
188 }
189 ExFreePool(List);
190 }
191 }
192 }
193
194 /*
195 * IntInvalidateWindows
196 *
197 * Internal function used by IntRedrawWindow.
198 */
199
200 VOID FASTCALL
201 IntInvalidateWindows(PWINDOW_OBJECT Window, HRGN hRgn, ULONG Flags)
202 {
203 INT RgnType;
204 BOOL HadPaintMessage, HadNCPaintMessage;
205 BOOL HasPaintMessage, HasNCPaintMessage;
206
207 /*
208 * Clip the given region with window rectangle (or region)
209 */
210
211 #ifdef TODO
212 if (!Window->WindowRegion)
213 #endif
214 {
215 HRGN hRgnWindow;
216
217 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
218 NtGdiOffsetRgn(hRgnWindow,
219 -Window->WindowRect.left,
220 -Window->WindowRect.top);
221 RgnType = NtGdiCombineRgn(hRgn, hRgn, hRgnWindow, RGN_AND);
222 NtGdiDeleteObject(hRgnWindow);
223 }
224 #ifdef TODO
225 else
226 {
227 RgnType = NtGdiCombineRgn(hRgn, hRgn, Window->WindowRegion, RGN_AND);
228 }
229 #endif
230
231 /*
232 * Save current state of pending updates
233 */
234
235 ExAcquireFastMutex(&Window->UpdateLock);
236 HadPaintMessage = Window->UpdateRegion != NULL ||
237 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
238 HadNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
239
240 /*
241 * Update the region and flags
242 */
243
244 if (Flags & RDW_INVALIDATE && RgnType != NULLREGION)
245 {
246 if (Window->UpdateRegion == NULL)
247 {
248 Window->UpdateRegion = NtGdiCreateRectRgn(0, 0, 0, 0);
249 }
250
251 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
252 hRgn, RGN_OR) == NULLREGION)
253 {
254 NtGdiDeleteObject(Window->UpdateRegion);
255 Window->UpdateRegion = NULL;
256 }
257
258 if (Flags & RDW_FRAME)
259 Window->Flags |= WINDOWOBJECT_NEED_NCPAINT;
260 if (Flags & RDW_ERASE)
261 Window->Flags |= WINDOWOBJECT_NEED_ERASEBKGND;
262
263 Flags |= RDW_FRAME;
264 }
265
266 if (Flags & RDW_VALIDATE && RgnType != NULLREGION)
267 {
268 if (Window->UpdateRegion != NULL)
269 {
270 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
271 hRgn, RGN_DIFF) == NULLREGION)
272 {
273 NtGdiDeleteObject(Window->UpdateRegion);
274 Window->UpdateRegion = NULL;
275 }
276 }
277
278 if (Window->UpdateRegion == NULL)
279 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
280 if (Flags & RDW_NOFRAME)
281 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
282 if (Flags & RDW_NOERASE)
283 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
284 }
285
286 if (Flags & RDW_INTERNALPAINT)
287 {
288 Window->Flags |= WINDOWOBJECT_NEED_INTERNALPAINT;
289 }
290
291 if (Flags & RDW_NOINTERNALPAINT)
292 {
293 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
294 }
295
296 /*
297 * Split the nonclient update region.
298 */
299
300 {
301 HRGN hRgnWindow, hRgnNonClient;
302
303 hRgnWindow = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
304 NtGdiOffsetRgn(hRgnWindow,
305 -Window->WindowRect.left,
306 -Window->WindowRect.top);
307
308 hRgnNonClient = NtGdiCreateRectRgn(0, 0, 0, 0);
309 if (NtGdiCombineRgn(hRgnNonClient, Window->UpdateRegion,
310 hRgnWindow, RGN_DIFF) == NULLREGION)
311 {
312 NtGdiDeleteObject(hRgnNonClient);
313 hRgnNonClient = NULL;
314 }
315
316 /*
317 * Remove the nonclient region from the standard update region.
318 */
319
320 if (NtGdiCombineRgn(Window->UpdateRegion, Window->UpdateRegion,
321 hRgnWindow, RGN_AND) == NULLREGION)
322 {
323 NtGdiDeleteObject(Window->UpdateRegion);
324 Window->UpdateRegion = NULL;
325 }
326
327 if (Window->NCUpdateRegion == NULL)
328 {
329 Window->NCUpdateRegion = hRgnNonClient;
330 }
331 else
332 {
333 NtGdiCombineRgn(Window->NCUpdateRegion, Window->NCUpdateRegion,
334 hRgnNonClient, RGN_OR);
335 }
336
337 NtGdiDeleteObject(hRgnWindow);
338 }
339
340 /*
341 * Process children if needed
342 */
343
344 if (!(Flags & RDW_NOCHILDREN) && !(Window->Style & WS_MINIMIZE) &&
345 ((Flags & RDW_ALLCHILDREN) || !(Window->Style & WS_CLIPCHILDREN)))
346 {
347 HWND *List, *phWnd;
348 PWINDOW_OBJECT Child;
349
350 if ((List = IntWinListChildren(Window)))
351 {
352 for (phWnd = List; *phWnd; ++phWnd)
353 {
354 Child = IntGetWindowObject(*phWnd);
355 if(!Child)
356 {
357 continue;
358 }
359 if (Child->Style & WS_VISIBLE)
360 {
361 /*
362 * Recursive call to update children UpdateRegion
363 */
364 HRGN hRgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
365 NtGdiCombineRgn(hRgnTemp, hRgn, 0, RGN_COPY);
366 NtGdiOffsetRgn(hRgnTemp,
367 Window->WindowRect.left - Child->WindowRect.left,
368 Window->WindowRect.top - Child->WindowRect.top);
369 IntInvalidateWindows(Child, hRgnTemp, Flags);
370 NtGdiDeleteObject(hRgnTemp);
371 }
372 IntReleaseWindowObject(Child);
373 }
374 ExFreePool(List);
375 }
376 }
377
378 /*
379 * Fake post paint messages to window message queue if needed
380 */
381
382 HasPaintMessage = Window->UpdateRegion != NULL ||
383 Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT;
384 HasNCPaintMessage = Window->Flags & WINDOWOBJECT_NEED_NCPAINT;
385
386 if (HasPaintMessage != HadPaintMessage)
387 {
388 if (HadPaintMessage)
389 MsqDecPaintCountQueue(Window->MessageQueue);
390 else
391 MsqIncPaintCountQueue(Window->MessageQueue);
392 }
393
394 if (HasNCPaintMessage != HadNCPaintMessage)
395 {
396 if (HadNCPaintMessage)
397 MsqDecPaintCountQueue(Window->MessageQueue);
398 else
399 MsqIncPaintCountQueue(Window->MessageQueue);
400 }
401
402 ExReleaseFastMutex(&Window->UpdateLock);
403 }
404
405 /*
406 * IntIsWindowDrawable
407 *
408 * Remarks
409 * Window is drawable when it is visible and all parents are not
410 * minimized.
411 */
412
413 BOOL FASTCALL
414 IntIsWindowDrawable(PWINDOW_OBJECT Window)
415 {
416 PWINDOW_OBJECT Old, Wnd = Window;
417
418 IntReferenceWindowObject(Wnd);
419 do
420 {
421 if (!(Wnd->Style & WS_VISIBLE) ||
422 ((Wnd->Style & WS_MINIMIZE) && (Wnd != Window)))
423 {
424 IntReleaseWindowObject(Wnd);
425 return FALSE;
426 }
427 Old = Wnd;
428 Wnd = IntGetParentObject(Wnd);
429 IntReleaseWindowObject(Old);
430 } while(Wnd);
431
432 return TRUE;
433 }
434
435 /*
436 * IntRedrawWindow
437 *
438 * Internal version of NtUserRedrawWindow that takes WINDOW_OBJECT as
439 * first parameter.
440 */
441
442 BOOL FASTCALL
443 IntRedrawWindow(PWINDOW_OBJECT Window, const RECT* UpdateRect, HRGN UpdateRgn,
444 ULONG Flags)
445 {
446 HRGN hRgn = NULL;
447
448 /*
449 * Step 1.
450 * Validation of passed parameters.
451 */
452
453 if (!IntIsWindowDrawable(Window) ||
454 (Flags & (RDW_VALIDATE | RDW_INVALIDATE)) ==
455 (RDW_VALIDATE | RDW_INVALIDATE))
456 {
457 return FALSE;
458 }
459
460 /*
461 * Step 2.
462 * Transform the parameters UpdateRgn and UpdateRect into
463 * a region hRgn specified in window coordinates.
464 */
465
466 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE))
467 {
468 if (UpdateRgn != NULL)
469 {
470 hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
471 NtGdiCombineRgn(hRgn, UpdateRgn, NULL, RGN_COPY);
472 NtGdiOffsetRgn(hRgn,
473 Window->ClientRect.left - Window->WindowRect.left,
474 Window->ClientRect.top - Window->WindowRect.top);
475 } else
476 if (UpdateRect != NULL)
477 {
478 hRgn = UnsafeIntCreateRectRgnIndirect((RECT *)UpdateRect);
479 NtGdiOffsetRgn(hRgn,
480 Window->ClientRect.left - Window->WindowRect.left,
481 Window->ClientRect.top - Window->WindowRect.top);
482 } else
483 if ((Flags & (RDW_INVALIDATE | RDW_FRAME)) == (RDW_INVALIDATE | RDW_FRAME) ||
484 (Flags & (RDW_VALIDATE | RDW_NOFRAME)) == (RDW_VALIDATE | RDW_NOFRAME))
485 {
486 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->WindowRect);
487 NtGdiOffsetRgn(hRgn,
488 -Window->WindowRect.left,
489 -Window->WindowRect.top);
490 }
491 else
492 {
493 hRgn = UnsafeIntCreateRectRgnIndirect(&Window->ClientRect);
494 NtGdiOffsetRgn(hRgn,
495 -Window->WindowRect.left,
496 -Window->WindowRect.top);
497 }
498 }
499
500 /*
501 * Step 3.
502 * Adjust the window update region depending on hRgn and flags.
503 */
504
505 if (Flags & (RDW_INVALIDATE | RDW_VALIDATE | RDW_INTERNALPAINT | RDW_NOINTERNALPAINT))
506 {
507 IntInvalidateWindows(Window, hRgn, Flags);
508 }
509
510 /*
511 * Step 4.
512 * Repaint and erase windows if needed.
513 */
514
515 if (Flags & (RDW_ERASENOW | RDW_UPDATENOW))
516 {
517 IntPaintWindows(Window, Flags);
518 }
519
520 /*
521 * Step 5.
522 * Cleanup ;-)
523 */
524
525 if (hRgn != NULL)
526 {
527 NtGdiDeleteObject(hRgn);
528 }
529
530 return TRUE;
531 }
532
533 BOOL FASTCALL
534 IntIsWindowDirty(PWINDOW_OBJECT Window)
535 {
536 return (Window->Style & WS_VISIBLE) &&
537 ((Window->UpdateRegion != NULL) ||
538 (Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT) ||
539 (Window->Flags & WINDOWOBJECT_NEED_NCPAINT));
540 }
541
542 HWND STDCALL
543 IntFindWindowToRepaint(HWND hWnd, PW32THREAD Thread)
544 {
545 PWINDOW_OBJECT Window;
546 PWINDOW_OBJECT Child;
547 HWND hFoundWnd = NULL;
548
549 Window = IntGetWindowObject(hWnd);
550 if (Window == NULL)
551 return NULL;
552
553 if (IntIsWindowDirty(Window) &&
554 IntWndBelongsToThread(Window, Thread))
555 {
556 IntReleaseWindowObject(Window);
557 return hWnd;
558 }
559
560 IntLockRelatives(Window);
561 for (Child = Window->FirstChild; Child; Child = Child->NextSibling)
562 {
563 if (IntIsWindowDirty(Child) &&
564 IntWndBelongsToThread(Child, Thread))
565 {
566 hFoundWnd = Child->Self;
567 break;
568 }
569 }
570 IntUnLockRelatives(Window);
571
572 if (hFoundWnd == NULL)
573 {
574 HWND *List;
575 INT i;
576
577 List = IntWinListChildren(Window);
578 if (List != NULL)
579 {
580 for (i = 0; List[i]; i++)
581 {
582 hFoundWnd = IntFindWindowToRepaint(List[i], Thread);
583 if (hFoundWnd != NULL)
584 break;
585 }
586 ExFreePool(List);
587 }
588 }
589
590 IntReleaseWindowObject(Window);
591
592 return hFoundWnd;
593 }
594
595 BOOL FASTCALL
596 IntGetPaintMessage(HWND hWnd, PW32THREAD Thread, MSG *Message,
597 BOOL Remove)
598 {
599 PWINDOW_OBJECT Window;
600 PUSER_MESSAGE_QUEUE MessageQueue = (PUSER_MESSAGE_QUEUE)Thread->MessageQueue;
601
602 if (!MessageQueue->PaintPosted)
603 return FALSE;
604
605 if (hWnd)
606 Message->hwnd = IntFindWindowToRepaint(hWnd, PsGetWin32Thread());
607 else
608 Message->hwnd = IntFindWindowToRepaint(IntGetDesktopWindow(), PsGetWin32Thread());
609
610 if (Message->hwnd == NULL)
611 {
612 #if 0
613 DPRINT1("PAINTING BUG: Thread marked as containing dirty windows, but no dirty windows found!\n");
614 #endif
615 ExAcquireFastMutex(&MessageQueue->Lock);
616 MessageQueue->PaintPosted = 0;
617 MessageQueue->PaintCount = 0;
618 ExReleaseFastMutex(&MessageQueue->Lock);
619 return FALSE;
620 }
621
622 Window = IntGetWindowObject(Message->hwnd);
623 if (Window != NULL)
624 {
625 ExAcquireFastMutex(&Window->UpdateLock);
626 if (Window->Flags & WINDOWOBJECT_NEED_NCPAINT)
627 {
628 Message->message = WM_NCPAINT;
629 Message->wParam = (WPARAM)Window->NCUpdateRegion;
630 Message->lParam = 0;
631 if (Remove)
632 {
633 IntValidateParent(Window, Window->NCUpdateRegion);
634 Window->NCUpdateRegion = NULL;
635 Window->Flags &= ~WINDOWOBJECT_NEED_NCPAINT;
636 MsqDecPaintCountQueue(Window->MessageQueue);
637 }
638 } else
639 {
640 Message->message = WM_PAINT;
641 Message->wParam = Message->lParam = 0;
642 if (Remove && Window->Flags & WINDOWOBJECT_NEED_INTERNALPAINT)
643 {
644 Window->Flags &= ~WINDOWOBJECT_NEED_INTERNALPAINT;
645 if (Window->UpdateRegion == NULL)
646 {
647 MsqDecPaintCountQueue(Window->MessageQueue);
648 }
649 }
650 }
651 ExReleaseFastMutex(&Window->UpdateLock);
652
653 IntReleaseWindowObject(Window);
654 return TRUE;
655 }
656
657 return FALSE;
658 }
659
660 HWND FASTCALL
661 IntFixCaret(HWND hWnd, LPRECT lprc, UINT flags)
662 {
663 PDESKTOP_OBJECT Desktop;
664 PTHRDCARETINFO CaretInfo;
665 HWND hWndCaret;
666
667 Desktop = PsGetCurrentThread()->Win32Thread->Desktop;
668 CaretInfo = ((PUSER_MESSAGE_QUEUE)Desktop->ActiveMessageQueue)->CaretInfo;
669 hWndCaret = CaretInfo->hWnd;
670 if (hWndCaret == hWnd ||
671 ((flags & SW_SCROLLCHILDREN) && IntIsChildWindow(hWnd, hWndCaret)))
672 {
673 POINT pt, FromOffset, ToOffset, Offset;
674 RECT rcCaret;
675
676 pt.x = CaretInfo->Pos.x;
677 pt.y = CaretInfo->Pos.y;
678 IntGetClientOrigin(hWndCaret, &FromOffset);
679 IntGetClientOrigin(hWnd, &ToOffset);
680 Offset.x = FromOffset.x - ToOffset.x;
681 Offset.y = FromOffset.y - ToOffset.y;
682 rcCaret.left = pt.x;
683 rcCaret.top = pt.y;
684 rcCaret.right = pt.x + CaretInfo->Size.cx;
685 rcCaret.bottom = pt.y + CaretInfo->Size.cy;
686 if (NtGdiIntersectRect(lprc, lprc, &rcCaret))
687 {
688 NtUserHideCaret(0);
689 lprc->left = pt.x;
690 lprc->top = pt.y;
691 return hWndCaret;
692 }
693 }
694
695 return 0;
696 }
697
698 /* PUBLIC FUNCTIONS ***********************************************************/
699
700 /*
701 * NtUserBeginPaint
702 *
703 * Status
704 * @implemented
705 */
706
707 HDC STDCALL
708 NtUserBeginPaint(HWND hWnd, PAINTSTRUCT* lPs)
709 {
710 PWINDOW_OBJECT Window;
711
712 if (!(Window = IntGetWindowObject(hWnd)))
713 {
714 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
715 return NULL;
716 }
717
718 NtUserHideCaret(hWnd);
719
720 lPs->hdc = NtUserGetDCEx(hWnd, 0, DCX_INTERSECTUPDATE | DCX_WINDOWPAINT |
721 DCX_USESTYLE);
722
723 if (!lPs->hdc)
724 {
725 IntReleaseWindowObject(Window);
726 return NULL;
727 }
728
729 ExAcquireFastMutex(&Window->UpdateLock);
730 if (Window->UpdateRegion != NULL)
731 {
732 MsqDecPaintCountQueue(Window->MessageQueue);
733 IntValidateParent(Window, Window->UpdateRegion);
734 NtGdiGetRgnBox(Window->UpdateRegion, &lPs->rcPaint);
735 NtGdiOffsetRect(&lPs->rcPaint,
736 Window->WindowRect.left - Window->ClientRect.left,
737 Window->WindowRect.top - Window->ClientRect.top);
738 NtGdiDeleteObject(Window->UpdateRegion);
739 Window->UpdateRegion = NULL;
740 }
741 else
742 {
743 NtUserGetClientRect(Window, &lPs->rcPaint);
744 }
745 ExReleaseFastMutex(&Window->UpdateLock);
746
747 if (Window->Flags & WINDOWOBJECT_NEED_ERASEBKGND)
748 {
749 Window->Flags &= ~WINDOWOBJECT_NEED_ERASEBKGND;
750 lPs->fErase = !IntSendMessage(hWnd, WM_ERASEBKGND, (WPARAM)lPs->hdc, 0);
751 }
752 else
753 {
754 lPs->fErase = FALSE;
755 }
756
757 IntReleaseWindowObject(Window);
758
759 return lPs->hdc;
760 }
761
762 /*
763 * NtUserEndPaint
764 *
765 * Status
766 * @implemented
767 */
768
769 BOOL STDCALL
770 NtUserEndPaint(HWND hWnd, CONST PAINTSTRUCT* lPs)
771 {
772 NtUserReleaseDC(hWnd, lPs->hdc);
773 NtUserShowCaret(hWnd);
774
775 return TRUE;
776 }
777
778 /*
779 * NtUserInvalidateRect
780 *
781 * Status
782 * @implemented
783 */
784
785 DWORD STDCALL
786 NtUserInvalidateRect(HWND hWnd, CONST RECT *Rect, BOOL Erase)
787 {
788 return NtUserRedrawWindow(hWnd, Rect, 0, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
789 }
790
791 /*
792 * NtUserInvalidateRgn
793 *
794 * Status
795 * @implemented
796 */
797
798 DWORD STDCALL
799 NtUserInvalidateRgn(HWND hWnd, HRGN Rgn, BOOL Erase)
800 {
801 return NtUserRedrawWindow(hWnd, NULL, Rgn, RDW_INVALIDATE | (Erase ? RDW_ERASE : 0));
802 }
803
804 /*
805 * NtUserValidateRgn
806 *
807 * Status
808 * @implemented
809 */
810
811 BOOL STDCALL
812 NtUserValidateRgn(HWND hWnd, HRGN hRgn)
813 {
814 return NtUserRedrawWindow(hWnd, NULL, hRgn, RDW_VALIDATE | RDW_NOCHILDREN);
815 }
816
817 /*
818 * NtUserUpdateWindow
819 *
820 * Status
821 * @implemented
822 */
823
824 BOOL STDCALL
825 NtUserUpdateWindow(HWND hWnd)
826 {
827 return NtUserRedrawWindow(hWnd, NULL, 0, RDW_UPDATENOW | RDW_ALLCHILDREN);
828 }
829
830 /*
831 * NtUserGetUpdateRgn
832 *
833 * Status
834 * @implemented
835 */
836
837 INT STDCALL
838 NtUserGetUpdateRgn(HWND hWnd, HRGN hRgn, BOOL bErase)
839 {
840 PWINDOW_OBJECT Window;
841 int RegionType;
842
843 if (!(Window = IntGetWindowObject(hWnd)))
844 {
845 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
846 return ERROR;
847 }
848
849 if (Window->UpdateRegion == NULL)
850 {
851 RegionType = (NtGdiSetRectRgn(hRgn, 0, 0, 0, 0) ? NULLREGION : ERROR);
852 }
853 else
854 {
855 RegionType = NtGdiCombineRgn(hRgn, Window->UpdateRegion, hRgn, RGN_COPY);
856 NtGdiOffsetRgn(
857 hRgn,
858 Window->WindowRect.left - Window->ClientRect.left,
859 Window->WindowRect.top - Window->ClientRect.top);
860 }
861
862 IntReleaseWindowObject(Window);
863
864 if (bErase && RegionType != NULLREGION && RegionType != ERROR)
865 {
866 NtUserRedrawWindow(hWnd, NULL, NULL, RDW_ERASENOW | RDW_NOCHILDREN);
867 }
868
869 return RegionType;
870 }
871
872 /*
873 * NtUserGetUpdateRect
874 *
875 * Status
876 * @implemented
877 */
878
879 BOOL STDCALL
880 NtUserGetUpdateRect(HWND hWnd, LPRECT lpRect, BOOL fErase)
881 {
882 HRGN hRgn = NtGdiCreateRectRgn(0, 0, 0, 0);
883
884 if (!lpRect)
885 {
886 SetLastWin32Error(ERROR_INVALID_PARAMETER);
887 return FALSE;
888 }
889
890 NtUserGetUpdateRgn(hWnd, hRgn, fErase);
891 NtGdiGetRgnBox(hRgn, lpRect);
892 NtGdiDeleteObject(hRgn);
893
894 return lpRect->left < lpRect->right && lpRect->top < lpRect->bottom;
895 }
896
897 /*
898 * NtUserRedrawWindow
899 *
900 * Status
901 * @implemented
902 */
903
904 BOOL STDCALL
905 NtUserRedrawWindow(HWND hWnd, CONST RECT *lprcUpdate, HRGN hrgnUpdate,
906 UINT flags)
907 {
908 RECT SafeUpdateRect;
909 NTSTATUS Status;
910 PWINDOW_OBJECT Wnd;
911
912 if (!(Wnd = IntGetWindowObject(hWnd ? hWnd : IntGetDesktopWindow())))
913 {
914 SetLastWin32Error(ERROR_INVALID_WINDOW_HANDLE);
915 return FALSE;
916 }
917
918 if (lprcUpdate != NULL)
919 {
920 Status = MmCopyFromCaller(&SafeUpdateRect, (PRECT)lprcUpdate,
921 sizeof(RECT));
922
923 if (!NT_SUCCESS(Status))
924 {
925 SetLastWin32Error(ERROR_INVALID_PARAMETER);
926 IntReleaseWindowObject(Wnd);
927 return FALSE;
928 }
929 }
930
931 Status = IntRedrawWindow(Wnd, NULL == lprcUpdate ? NULL : &SafeUpdateRect,
932 hrgnUpdate, flags);
933
934 if (!NT_SUCCESS(Status))
935 {
936 /* IntRedrawWindow fails only in case that flags are invalid */
937 SetLastWin32Error(ERROR_INVALID_PARAMETER);
938 IntReleaseWindowObject(Wnd);
939 return FALSE;
940 }
941
942 IntReleaseWindowObject(Wnd);
943 return TRUE;
944 }
945
946 /*
947 * NtUserScrollDC
948 *
949 * Status
950 * @implemented
951 */
952
953 DWORD STDCALL
954 NtUserScrollDC(HDC hDC, INT dx, INT dy, const RECT *lprcScroll,
955 const RECT *lprcClip, HRGN hrgnUpdate, LPRECT lprcUpdate)
956 {
957 RECT rSrc, rClipped_src, rClip, rDst, offset;
958 PDC DC;
959
960 /*
961 * Compute device clipping region (in device coordinates).
962 */
963
964 DC = DC_LockDc(hDC);
965 if (NULL == DC)
966 {
967 return FALSE;
968 }
969 if (lprcScroll)
970 rSrc = *lprcScroll;
971 else
972 IntGdiGetClipBox(hDC, &rSrc);
973 IntLPtoDP(DC, (LPPOINT)&rSrc, 2);
974
975 if (lprcClip)
976 rClip = *lprcClip;
977 else
978 IntGdiGetClipBox(hDC, &rClip);
979 IntLPtoDP(DC, (LPPOINT)&rClip, 2);
980
981 NtGdiIntersectRect(&rClipped_src, &rSrc, &rClip);
982
983 rDst = rClipped_src;
984 NtGdiSetRect(&offset, 0, 0, dx, dy);
985 IntLPtoDP(DC, (LPPOINT)&offset, 2);
986 NtGdiOffsetRect(&rDst, offset.right - offset.left, offset.bottom - offset.top);
987 NtGdiIntersectRect(&rDst, &rDst, &rClip);
988
989 /*
990 * Copy bits, if possible.
991 */
992
993 if (rDst.bottom > rDst.top && rDst.right > rDst.left)
994 {
995 RECT rDst_lp = rDst, rSrc_lp = rDst;
996
997 NtGdiOffsetRect(&rSrc_lp, offset.left - offset.right, offset.top - offset.bottom);
998 IntDPtoLP(DC, (LPPOINT)&rDst_lp, 2);
999 IntDPtoLP(DC, (LPPOINT)&rSrc_lp, 2);
1000 DC_UnlockDc(hDC);
1001
1002 if (!NtGdiBitBlt(hDC, rDst_lp.left, rDst_lp.top, rDst_lp.right - rDst_lp.left,
1003 rDst_lp.bottom - rDst_lp.top, hDC, rSrc_lp.left, rSrc_lp.top,
1004 SRCCOPY))
1005 return FALSE;
1006 }
1007 else
1008 {
1009 DC_UnlockDc(hDC);
1010 }
1011
1012 /*
1013 * Compute update areas. This is the clipped source or'ed with the
1014 * unclipped source translated minus the clipped src translated (rDst)
1015 * all clipped to rClip.
1016 */
1017
1018 if (hrgnUpdate || lprcUpdate)
1019 {
1020 HRGN hRgn = hrgnUpdate, hRgn2;
1021
1022 if (hRgn)
1023 NtGdiSetRectRgn(hRgn, rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1024 else
1025 hRgn = NtGdiCreateRectRgn(rClipped_src.left, rClipped_src.top, rClipped_src.right, rClipped_src.bottom);
1026
1027 hRgn2 = UnsafeIntCreateRectRgnIndirect(&rSrc);
1028 NtGdiOffsetRgn(hRgn2, offset.right - offset.left, offset.bottom - offset.top);
1029 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_OR);
1030
1031 NtGdiSetRectRgn(hRgn2, rDst.left, rDst.top, rDst.right, rDst.bottom);
1032 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_DIFF);
1033
1034 NtGdiSetRectRgn(hRgn2, rClip.left, rClip.top, rClip.right, rClip.bottom);
1035 NtGdiCombineRgn(hRgn, hRgn, hRgn2, RGN_AND);
1036
1037 if (lprcUpdate)
1038 {
1039 NtGdiGetRgnBox(hRgn, lprcUpdate);
1040
1041 /* Put the lprcUpdate in logical coordinate */
1042 NtGdiDPtoLP(hDC, (LPPOINT)lprcUpdate, 2);
1043 }
1044 if (!hrgnUpdate)
1045 NtGdiDeleteObject(hRgn);
1046 NtGdiDeleteObject(hRgn2);
1047 }
1048 return TRUE;
1049 }
1050
1051 /*
1052 * NtUserScrollWindowEx
1053 *
1054 * Status
1055 * @implemented
1056 */
1057
1058 DWORD STDCALL
1059 NtUserScrollWindowEx(HWND hWnd, INT dx, INT dy, const RECT *rect,
1060 const RECT *clipRect, HRGN hrgnUpdate, LPRECT rcUpdate, UINT flags)
1061 {
1062 RECT rc, cliprc, caretrc;
1063 INT Result;
1064 PWINDOW_OBJECT Window;
1065 HDC hDC;
1066 HRGN hrgnTemp;
1067 HWND hwndCaret;
1068 BOOL bUpdate = (rcUpdate || hrgnUpdate || flags & (SW_INVALIDATE | SW_ERASE));
1069 BOOL bOwnRgn = TRUE;
1070
1071 Window = IntGetWindowObject(hWnd);
1072 if (!Window || !IntIsWindowDrawable(Window))
1073 {
1074 IntReleaseWindowObject(Window);
1075 return ERROR;
1076 }
1077
1078 IntGetClientRect(Window, &rc);
1079 if (rect)
1080 NtGdiIntersectRect(&rc, &rc, rect);
1081
1082 if (clipRect)
1083 NtGdiIntersectRect(&cliprc, &rc, clipRect);
1084 else
1085 cliprc = rc;
1086
1087 if (cliprc.right <= cliprc.left || cliprc.bottom <= cliprc.top ||
1088 (dx == 0 && dy == 0))
1089 {
1090 return NULLREGION;
1091 }
1092
1093 caretrc = rc;
1094 hwndCaret = IntFixCaret(hWnd, &caretrc, flags);
1095
1096 if (hrgnUpdate)
1097 bOwnRgn = FALSE;
1098 else if (bUpdate)
1099 hrgnUpdate = NtGdiCreateRectRgn(0, 0, 0, 0);
1100
1101 hDC = NtUserGetDCEx(hWnd, 0, DCX_CACHE | DCX_USESTYLE);
1102 if (hDC)
1103 {
1104 NtUserScrollDC(hDC, dx, dy, &rc, &cliprc, hrgnUpdate, rcUpdate);
1105 NtUserReleaseDC(hWnd, hDC);
1106 }
1107
1108 /*
1109 * Take into account the fact that some damage may have occurred during
1110 * the scroll.
1111 */
1112
1113 hrgnTemp = NtGdiCreateRectRgn(0, 0, 0, 0);
1114 Result = NtUserGetUpdateRgn(hWnd, hrgnTemp, FALSE);
1115 if (Result != NULLREGION)
1116 {
1117 HRGN hrgnClip = UnsafeIntCreateRectRgnIndirect(&cliprc);
1118 NtGdiOffsetRgn(hrgnTemp, dx, dy);
1119 NtGdiCombineRgn(hrgnTemp, hrgnTemp, hrgnClip, RGN_AND);
1120 NtUserRedrawWindow(hWnd, NULL, hrgnTemp, RDW_INVALIDATE | RDW_ERASE);
1121 NtGdiDeleteObject(hrgnClip);
1122 }
1123 NtGdiDeleteObject(hrgnTemp);
1124
1125 if (flags & SW_SCROLLCHILDREN)
1126 {
1127 HWND *List = IntWinListChildren(Window);
1128 if (List)
1129 {
1130 int i;
1131 RECT r, dummy;
1132 POINT ClientOrigin;
1133
1134 for (i = 0; List[i]; i++)
1135 {
1136 NtUserGetWindowRect(List[i], &r);
1137 IntGetClientOrigin(hWnd, &ClientOrigin);
1138 r.left -= ClientOrigin.x;
1139 r.top -= ClientOrigin.y;
1140 r.right -= ClientOrigin.x;
1141 r.bottom -= ClientOrigin.y;
1142 if (!rect || NtGdiIntersectRect(&dummy, &r, &rc))
1143 WinPosSetWindowPos(List[i], 0, r.left + dx, r.top + dy, 0, 0,
1144 SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE |
1145 SWP_NOREDRAW);
1146 }
1147 ExFreePool(List);
1148 }
1149 }
1150
1151 if (flags & (SW_INVALIDATE | SW_ERASE))
1152 NtUserRedrawWindow(hWnd, NULL, hrgnUpdate, RDW_INVALIDATE | RDW_ERASE |
1153 ((flags & SW_ERASE) ? RDW_ERASENOW : 0) |
1154 ((flags & SW_SCROLLCHILDREN) ? RDW_ALLCHILDREN : 0));
1155
1156 if (bOwnRgn && hrgnUpdate)
1157 NtGdiDeleteObject(hrgnUpdate);
1158
1159 if (hwndCaret)
1160 {
1161 IntSetCaretPos(caretrc.left + dx, caretrc.top + dy);
1162 NtUserShowCaret(hwndCaret);
1163 }
1164
1165 IntReleaseWindowObject(Window);
1166
1167 return Result;
1168 }
1169
1170 /* EOF */