WM_WINDOWPOSCHANGED should always contain a final window position.
[wine.git] / server / region.c
blob7ecefe99a1f8a6de22e3ad5d5f8cb01dc27a0848
1 /*
2 * Server-side region objects. Based on the X11 implementation.
4 * Copyright 1993, 1994, 1995, 2004 Alexandre Julliard
5 * Modifications and additions: Copyright 1998 Huw Davies
6 * 1999 Alex Korobka
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 * Note:
23 * This is a simplified version of the code, without all the explanations.
24 * Check the equivalent GDI code to make sense of it.
27 /************************************************************************
29 Copyright (c) 1987, 1988 X Consortium
31 Permission is hereby granted, free of charge, to any person obtaining a copy
32 of this software and associated documentation files (the "Software"), to deal
33 in the Software without restriction, including without limitation the rights
34 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
35 copies of the Software, and to permit persons to whom the Software is
36 furnished to do so, subject to the following conditions:
38 The above copyright notice and this permission notice shall be included in
39 all copies or substantial portions of the Software.
41 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
42 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
43 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
44 X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
45 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
46 CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
48 Except as contained in this notice, the name of the X Consortium shall not be
49 used in advertising or otherwise to promote the sale, use or other dealings
50 in this Software without prior written authorization from the X Consortium.
53 Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts.
55 All Rights Reserved
57 Permission to use, copy, modify, and distribute this software and its
58 documentation for any purpose and without fee is hereby granted,
59 provided that the above copyright notice appear in all copies and that
60 both that copyright notice and this permission notice appear in
61 supporting documentation, and that the name of Digital not be
62 used in advertising or publicity pertaining to distribution of the
63 software without specific, written prior permission.
65 DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
66 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
67 DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
68 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
69 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
70 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
71 SOFTWARE.
73 ************************************************************************/
75 #include <stdarg.h>
76 #include <stdlib.h>
77 #include <string.h>
78 #include "request.h"
80 struct region
82 int size;
83 int num_rects;
84 rectangle_t *rects;
85 rectangle_t extents;
89 #define RGN_DEFAULT_RECTS 2
91 #define EXTENTCHECK(r1, r2) \
92 ((r1)->right > (r2)->left && \
93 (r1)->left < (r2)->right && \
94 (r1)->bottom > (r2)->top && \
95 (r1)->top < (r2)->bottom)
97 typedef int (*overlap_func_t)( struct region *reg, const rectangle_t *r1, const rectangle_t *r1End,
98 const rectangle_t *r2, const rectangle_t *r2End, int top, int bottom );
99 typedef int (*non_overlap_func_t)( struct region *reg, const rectangle_t *r,
100 const rectangle_t *rEnd, int top, int bottom );
102 /* add a rectangle to a region */
103 static inline rectangle_t *add_rect( struct region *reg )
105 if (reg->num_rects >= reg->size - 1)
107 rectangle_t *new_rect = realloc( reg->rects, 2 * sizeof(rectangle_t) * reg->size );
108 if (!new_rect)
110 set_error( STATUS_NO_MEMORY );
111 return 0;
113 reg->rects = new_rect;
114 reg->size *= 2;
116 return reg->rects + reg->num_rects++;
119 /* make sure all the rectangles are valid and that the region is properly y-x-banded */
120 static inline int validate_rectangles( const rectangle_t *rects, unsigned int nb_rects )
122 const rectangle_t *ptr, *end;
124 for (ptr = rects, end = rects + nb_rects; ptr < end; ptr++)
126 if (ptr->left >= ptr->right || ptr->top >= ptr->bottom) return 0; /* empty rectangle */
127 if (ptr == end - 1) break;
128 if (ptr[0].top == ptr[1].top) /* same band */
130 if (ptr[0].bottom != ptr[1].bottom) return 0; /* not same y extent */
131 if (ptr[0].right >= ptr[1].left) return 0; /* not properly x ordered */
133 else /* new band */
135 if (ptr[0].bottom > ptr[1].top) return 0; /* not properly y ordered */
138 return 1;
141 /* attempt to merge the rects in the current band with those in the */
142 /* previous one. Used only by region_op. */
143 static int coalesce_region( struct region *pReg, int prevStart, int curStart )
145 int curNumRects;
146 rectangle_t *pRegEnd = &pReg->rects[pReg->num_rects];
147 rectangle_t *pPrevRect = &pReg->rects[prevStart];
148 rectangle_t *pCurRect = &pReg->rects[curStart];
149 int prevNumRects = curStart - prevStart;
150 int bandtop = pCurRect->top;
152 for (curNumRects = 0;
153 (pCurRect != pRegEnd) && (pCurRect->top == bandtop);
154 curNumRects++)
156 pCurRect++;
159 if (pCurRect != pRegEnd)
161 pRegEnd--;
162 while (pRegEnd[-1].top == pRegEnd->top) pRegEnd--;
163 curStart = pRegEnd - pReg->rects;
164 pRegEnd = pReg->rects + pReg->num_rects;
167 if ((curNumRects == prevNumRects) && (curNumRects != 0))
169 pCurRect -= curNumRects;
170 if (pPrevRect->bottom == pCurRect->top)
174 if ((pPrevRect->left != pCurRect->left) ||
175 (pPrevRect->right != pCurRect->right)) return curStart;
176 pPrevRect++;
177 pCurRect++;
178 prevNumRects -= 1;
179 } while (prevNumRects != 0);
181 pReg->num_rects -= curNumRects;
182 pCurRect -= curNumRects;
183 pPrevRect -= curNumRects;
187 pPrevRect->bottom = pCurRect->bottom;
188 pPrevRect++;
189 pCurRect++;
190 curNumRects -= 1;
191 } while (curNumRects != 0);
193 if (pCurRect == pRegEnd) curStart = prevStart;
194 else do { *pPrevRect++ = *pCurRect++; } while (pCurRect != pRegEnd);
198 return curStart;
201 /* apply an operation to two regions */
202 /* check the GDI version of the code for explanations */
203 static int region_op( struct region *newReg, const struct region *reg1, const struct region *reg2,
204 overlap_func_t overlap_func,
205 non_overlap_func_t non_overlap1_func,
206 non_overlap_func_t non_overlap2_func )
208 int ybot, ytop, top, bot, prevBand, curBand;
209 const rectangle_t *r1BandEnd, *r2BandEnd;
211 const rectangle_t *r1 = reg1->rects;
212 const rectangle_t *r2 = reg2->rects;
213 const rectangle_t *r1End = r1 + reg1->num_rects;
214 const rectangle_t *r2End = r2 + reg2->num_rects;
216 rectangle_t *new_rects, *old_rects = newReg->rects;
217 int new_size, ret = 0;
219 new_size = max( reg1->num_rects, reg2->num_rects ) * 2;
220 if (!(new_rects = mem_alloc( new_size * sizeof(*newReg->rects) ))) return 0;
222 newReg->size = new_size;
223 newReg->rects = new_rects;
224 newReg->num_rects = 0;
226 if (reg1->extents.top < reg2->extents.top)
227 ybot = reg1->extents.top;
228 else
229 ybot = reg2->extents.top;
231 prevBand = 0;
235 curBand = newReg->num_rects;
237 r1BandEnd = r1;
238 while ((r1BandEnd != r1End) && (r1BandEnd->top == r1->top)) r1BandEnd++;
240 r2BandEnd = r2;
241 while ((r2BandEnd != r2End) && (r2BandEnd->top == r2->top)) r2BandEnd++;
243 if (r1->top < r2->top)
245 top = max(r1->top,ybot);
246 bot = min(r1->bottom,r2->top);
248 if ((top != bot) && non_overlap1_func)
250 if (!non_overlap1_func( newReg, r1, r1BandEnd, top, bot )) goto done;
253 ytop = r2->top;
255 else if (r2->top < r1->top)
257 top = max(r2->top,ybot);
258 bot = min(r2->bottom,r1->top);
260 if ((top != bot) && non_overlap2_func)
262 if (!non_overlap2_func( newReg, r2, r2BandEnd, top, bot )) goto done;
265 ytop = r1->top;
267 else
269 ytop = r1->top;
272 if (newReg->num_rects != curBand)
273 prevBand = coalesce_region(newReg, prevBand, curBand);
275 ybot = min(r1->bottom, r2->bottom);
276 curBand = newReg->num_rects;
277 if (ybot > ytop)
279 if (!overlap_func( newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot )) goto done;
282 if (newReg->num_rects != curBand)
283 prevBand = coalesce_region(newReg, prevBand, curBand);
285 if (r1->bottom == ybot) r1 = r1BandEnd;
286 if (r2->bottom == ybot) r2 = r2BandEnd;
287 } while ((r1 != r1End) && (r2 != r2End));
289 curBand = newReg->num_rects;
290 if (r1 != r1End)
292 if (non_overlap1_func)
296 r1BandEnd = r1;
297 while ((r1BandEnd < r1End) && (r1BandEnd->top == r1->top)) r1BandEnd++;
298 if (!non_overlap1_func( newReg, r1, r1BandEnd, max(r1->top,ybot), r1->bottom ))
299 goto done;
300 r1 = r1BandEnd;
301 } while (r1 != r1End);
304 else if ((r2 != r2End) && non_overlap2_func)
308 r2BandEnd = r2;
309 while ((r2BandEnd < r2End) && (r2BandEnd->top == r2->top)) r2BandEnd++;
310 if (!non_overlap2_func( newReg, r2, r2BandEnd, max(r2->top,ybot), r2->bottom ))
311 goto done;
312 r2 = r2BandEnd;
313 } while (r2 != r2End);
316 if (newReg->num_rects != curBand) coalesce_region(newReg, prevBand, curBand);
318 if ((newReg->num_rects < (newReg->size / 2)) && (newReg->size > 2))
320 new_size = max( newReg->num_rects, RGN_DEFAULT_RECTS );
321 if ((new_rects = realloc( newReg->rects, sizeof(*newReg->rects) * new_size )))
323 newReg->rects = new_rects;
324 newReg->size = new_size;
327 ret = 1;
328 done:
329 free( old_rects );
330 return ret;
333 /* recalculate the extents of a region */
334 static void set_region_extents( struct region *region )
336 rectangle_t *pRect, *pRectEnd;
338 if (region->num_rects == 0)
340 region->extents.left = 0;
341 region->extents.top = 0;
342 region->extents.right = 0;
343 region->extents.bottom = 0;
344 return;
347 pRect = region->rects;
348 pRectEnd = &pRect[region->num_rects - 1];
350 region->extents.left = pRect->left;
351 region->extents.top = pRect->top;
352 region->extents.right = pRectEnd->right;
353 region->extents.bottom = pRectEnd->bottom;
355 while (pRect <= pRectEnd)
357 if (pRect->left < region->extents.left) region->extents.left = pRect->left;
358 if (pRect->right > region->extents.right) region->extents.right = pRect->right;
359 pRect++;
363 /* handle an overlapping band for intersect_region */
364 static int intersect_overlapping( struct region *pReg,
365 const rectangle_t *r1, const rectangle_t *r1End,
366 const rectangle_t *r2, const rectangle_t *r2End,
367 int top, int bottom )
370 int left, right;
372 while ((r1 != r1End) && (r2 != r2End))
374 left = max(r1->left, r2->left);
375 right = min(r1->right, r2->right);
377 if (left < right)
379 rectangle_t *rect = add_rect( pReg );
380 if (!rect) return 0;
381 rect->left = left;
382 rect->top = top;
383 rect->right = right;
384 rect->bottom = bottom;
387 if (r1->right < r2->right) r1++;
388 else if (r2->right < r1->right) r2++;
389 else
391 r1++;
392 r2++;
395 return 1;
398 /* handle a non-overlapping band for subtract_region */
399 static int subtract_non_overlapping( struct region *pReg, const rectangle_t *r,
400 const rectangle_t *rEnd, int top, int bottom )
402 while (r != rEnd)
404 rectangle_t *rect = add_rect( pReg );
405 if (!rect) return 0;
406 rect->left = r->left;
407 rect->top = top;
408 rect->right = r->right;
409 rect->bottom = bottom;
410 r++;
412 return 1;
415 /* handle an overlapping band for subtract_region */
416 static int subtract_overlapping( struct region *pReg,
417 const rectangle_t *r1, const rectangle_t *r1End,
418 const rectangle_t *r2, const rectangle_t *r2End,
419 int top, int bottom )
421 int left = r1->left;
423 while ((r1 != r1End) && (r2 != r2End))
425 if (r2->right <= left) r2++;
426 else if (r2->left <= left)
428 left = r2->right;
429 if (left >= r1->right)
431 r1++;
432 if (r1 != r1End)
433 left = r1->left;
435 else r2++;
437 else if (r2->left < r1->right)
439 rectangle_t *rect = add_rect( pReg );
440 if (!rect) return 0;
441 rect->left = left;
442 rect->top = top;
443 rect->right = r2->left;
444 rect->bottom = bottom;
445 left = r2->right;
446 if (left >= r1->right)
448 r1++;
449 if (r1 != r1End)
450 left = r1->left;
452 else r2++;
454 else
456 if (r1->right > left)
458 rectangle_t *rect = add_rect( pReg );
459 if (!rect) return 0;
460 rect->left = left;
461 rect->top = top;
462 rect->right = r1->right;
463 rect->bottom = bottom;
465 r1++;
466 left = r1->left;
470 while (r1 != r1End)
472 rectangle_t *rect = add_rect( pReg );
473 if (!rect) return 0;
474 rect->left = left;
475 rect->top = top;
476 rect->right = r1->right;
477 rect->bottom = bottom;
478 r1++;
479 if (r1 != r1End) left = r1->left;
481 return 1;
484 /* handle a non-overlapping band for union_region */
485 static int union_non_overlapping( struct region *pReg, const rectangle_t *r,
486 const rectangle_t *rEnd, int top, int bottom )
488 while (r != rEnd)
490 rectangle_t *rect = add_rect( pReg );
491 if (!rect) return 0;
492 rect->left = r->left;
493 rect->top = top;
494 rect->right = r->right;
495 rect->bottom = bottom;
496 r++;
498 return 1;
501 /* handle an overlapping band for union_region */
502 static int union_overlapping( struct region *pReg,
503 const rectangle_t *r1, const rectangle_t *r1End,
504 const rectangle_t *r2, const rectangle_t *r2End,
505 int top, int bottom )
507 #define MERGERECT(r) \
508 if ((pReg->num_rects != 0) && \
509 (pReg->rects[pReg->num_rects-1].top == top) && \
510 (pReg->rects[pReg->num_rects-1].bottom == bottom) && \
511 (pReg->rects[pReg->num_rects-1].right >= r->left)) \
513 if (pReg->rects[pReg->num_rects-1].right < r->right) \
515 pReg->rects[pReg->num_rects-1].right = r->right; \
518 else \
520 rectangle_t *rect = add_rect( pReg ); \
521 if (!rect) return 0; \
522 rect->top = top; \
523 rect->bottom = bottom; \
524 rect->left = r->left; \
525 rect->right = r->right; \
527 r++;
529 while ((r1 != r1End) && (r2 != r2End))
531 if (r1->left < r2->left)
533 MERGERECT(r1);
535 else
537 MERGERECT(r2);
541 if (r1 != r1End)
545 MERGERECT(r1);
546 } while (r1 != r1End);
548 else while (r2 != r2End)
550 MERGERECT(r2);
552 return 1;
553 #undef MERGERECT
557 /* create a region from an array of rectangles */
558 struct region *create_region( const rectangle_t *rects, unsigned int nb_rects )
560 struct region *region;
561 unsigned int size = max( nb_rects, RGN_DEFAULT_RECTS );
563 if (!validate_rectangles( rects, nb_rects ))
565 set_error( STATUS_INVALID_PARAMETER );
566 return NULL;
568 if (!(region = mem_alloc( sizeof(*region) ))) return NULL;
569 if (!(region->rects = mem_alloc( size * sizeof(*region->rects) )))
571 free( region );
572 return NULL;
574 region->size = size;
575 region->num_rects = nb_rects;
576 memcpy( region->rects, rects, nb_rects * sizeof(*rects) );
577 set_region_extents( region );
578 return region;
582 /* free a region */
583 void free_region( struct region *region )
585 free( region->rects );
586 free( region );
589 /* set region to a simple rectangle */
590 void set_region_rect( struct region *region, const rectangle_t *rect )
592 if (rect->left < rect->right && rect->top < rect->bottom)
594 region->num_rects = 1;
595 region->rects[0] = region->extents = *rect;
597 else
599 region->num_rects = 0;
600 region->extents.left = 0;
601 region->extents.top = 0;
602 region->extents.right = 0;
603 region->extents.bottom = 0;
607 /* retrieve the region data for sending to the client */
608 rectangle_t *get_region_data( const struct region *region, size_t *total_size )
610 *total_size = region->num_rects * sizeof(rectangle_t);
611 return memdup( region->rects, *total_size );
614 /* retrieve the region data for sending to the client and free the region at the same time */
615 rectangle_t *get_region_data_and_free( struct region *region, size_t *total_size )
617 rectangle_t *ret = region->rects;
618 *total_size = region->num_rects * sizeof(rectangle_t);
619 free( region );
620 return ret;
623 /* check if a given region is empty */
624 int is_region_empty( const struct region *region )
626 return region->num_rects == 0;
630 /* get the extents rect of a region */
631 void get_region_extents( const struct region *region, rectangle_t *rect )
633 *rect = region->extents;
636 /* add an offset to a region */
637 void offset_region( struct region *region, int x, int y )
639 rectangle_t *rect, *end;
641 for (rect = region->rects, end = rect + region->num_rects; rect < end; rect++)
643 rect->left += x;
644 rect->right += x;
645 rect->top += y;
646 rect->bottom += y;
648 region->extents.left += x;
649 region->extents.right += x;
650 region->extents.top += y;
651 region->extents.bottom += y;
654 /* make a copy of a region; returns dst or NULL on error */
655 struct region *copy_region( struct region *dst, const struct region *src )
657 if (dst == src) return dst;
659 if (dst->size < src->num_rects)
661 rectangle_t *rect = realloc( dst->rects, src->num_rects * sizeof(*rect) );
662 if (!rect)
664 set_error( STATUS_NO_MEMORY );
665 return NULL;
667 dst->rects = rect;
668 dst->size = src->num_rects;
670 dst->num_rects = src->num_rects;
671 dst->extents = src->extents;
672 memcpy( dst->rects, src->rects, src->num_rects * sizeof(*dst->rects) );
673 return dst;
676 /* compute the intersection of two regions into dst, which can be one of the source regions */
677 struct region *intersect_region( struct region *dst, const struct region *src1,
678 const struct region *src2 )
680 if (!src1->num_rects || !src2->num_rects || !EXTENTCHECK(&src1->extents, &src2->extents))
682 dst->num_rects = 0;
683 dst->extents.left = 0;
684 dst->extents.top = 0;
685 dst->extents.right = 0;
686 dst->extents.bottom = 0;
687 return dst;
689 if (!region_op( dst, src1, src2, intersect_overlapping, NULL, NULL )) return NULL;
690 set_region_extents( dst );
691 return dst;
694 /* compute the subtraction of two regions into dst, which can be one of the source regions */
695 struct region *subtract_region( struct region *dst, const struct region *src1,
696 const struct region *src2 )
698 if (!src1->num_rects || !src2->num_rects || !EXTENTCHECK(&src1->extents, &src2->extents))
699 return copy_region( dst, src1 );
701 if (!region_op( dst, src1, src2, subtract_overlapping,
702 subtract_non_overlapping, NULL )) return NULL;
703 set_region_extents( dst );
704 return dst;
707 /* compute the union of two regions into dst, which can be one of the source regions */
708 struct region *union_region( struct region *dst, const struct region *src1,
709 const struct region *src2 )
711 if (src1 == src2) return copy_region( dst, src1 );
712 if (!src1->num_rects) return copy_region( dst, src2 );
713 if (!src2->num_rects) return copy_region( dst, src1 );
715 if ((src1->num_rects == 1) &&
716 (src1->extents.left <= src2->extents.left) &&
717 (src1->extents.top <= src2->extents.top) &&
718 (src1->extents.right >= src2->extents.right) &&
719 (src1->extents.bottom >= src2->extents.bottom))
720 return copy_region( dst, src1 );
722 if ((src2->num_rects == 1) &&
723 (src2->extents.left <= src1->extents.left) &&
724 (src2->extents.top <= src1->extents.top) &&
725 (src2->extents.right >= src1->extents.right) &&
726 (src2->extents.bottom >= src1->extents.bottom))
727 return copy_region( dst, src2 );
729 if (!region_op( dst, src1, src2, union_overlapping,
730 union_non_overlapping, union_non_overlapping )) return NULL;
732 dst->extents.left = min(src1->extents.left, src2->extents.left);
733 dst->extents.top = min(src1->extents.top, src2->extents.top);
734 dst->extents.right = max(src1->extents.right, src2->extents.right);
735 dst->extents.bottom = max(src1->extents.bottom, src2->extents.bottom);
736 return dst;