2 * multivis.c - Mechanism for GetImage across Multiple Visuals
7 * Sun Microsystems, Inc.
13 /* Copyright (c) 1990, 2011, Oracle and/or its affiliates. All rights reserved.
15 * Permission is hereby granted, free of charge, to any person obtaining a
16 * copy of this software and associated documentation files (the "Software"),
17 * to deal in the Software without restriction, including without limitation
18 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
19 * and/or sell copies of the Software, and to permit persons to whom the
20 * Software is furnished to do so, subject to the following conditions:
22 * The above copyright notice and this permission notice (including the next
23 * paragraph) shall be included in all copies or substantial portions of the
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
29 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
31 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
32 * DEALINGS IN THE SOFTWARE.
37 #include <X11/Xutil.h>
39 #include <X11/extensions/shape.h>
43 static MVColmap
*mvFindColormap(Colormap cmap
);
44 static XVisualInfo
*mvMatchVisual(VisualID vid
);
45 static void mvGetColormap(MVWinVisInfo
*pWI
);
46 static void mvCalculateComposite(MVWinVisInfo
*pWI
);
47 static unsigned long mvCompositePixel(unsigned long i
, MVColmap
*pCmp
);
49 static Display
*mvDpy
; /* Display */
50 static int mvScreen
; /* Screen */
51 static XVisualInfo
*mvVlist
; /* List of Visuals */
52 static int mvNumVis
; /* Number of visuals in list */
54 static Bool mvShape
; /* Shape extension ? */
57 static MVWinVisInfoList winList
; /* Here we'll grow a list of windows
58 Sorted back to front */
60 static MVColmap
*colMaps
; /* list of colormaps we encounter */
61 static MVPel
*mvImg
; /* mvImg is what we compose the image
64 static int request_width
,
65 request_height
; /* size of requested rectangle */
66 static int request_x
, request_y
; /* The top left of requested
67 rectangle in Root Space */
70 static void *mvCallbackData
;
71 static mvCallbackFunc mvCallbackFunction
;
72 #endif /* UPDATE_HACK */
76 * Initialise the mvLib routines ...
87 mvCallbackFunc callbackFunction
)
94 #endif /* UPDATE_HACK */
99 /* Initialise screen info */
105 mvShape
= XShapeQueryExtension(dpy
, &tmp
, &tmp
);
109 mvCallbackData
= callbackData
;
110 mvCallbackFunction
= callbackFunction
;
111 #endif /* UPDATE_HACK */
115 * Create an Img.. Cleared to zeros.
116 * returns 0 if failure, non-zero for success.
117 * Note that it is the reponsibility of the caller
118 * to verify that any resulting XGetImage will
119 * be within the bounds of the screen.
120 * i.e, x, y, wd, ht must be such that the rectangle
121 * is fully within the bounds of the screen.
124 mvCreatImg(int wd
, int ht
, int x
, int y
)
131 if ((mvImg
= (MVPel
*) calloc(sizeof(MVPel
), request_width
* request_height
))
139 * Reset the mvLib routines
155 if (winList
.wins
[i
].region
)
156 XDestroyRegion(winList
.wins
[i
].region
);
161 winList
.used
= winList
.allocated
= 0;
163 /* Clean colmap list */
167 colMaps
= pCmap
->next
;
175 * Recursively walk the window tree.
176 * Find windows that intersect the requested region.
177 * Create a list of such windows in global winList.
178 * Assumes winList was cleared beforehand.
182 Window win
, /* This window */
183 int px
, int py
, /* parent's origin in root space */
184 int x
, int y
, /* Top left of requested rectangle in root space */
185 int wi
, int hi
, /* size of requested rectangle */
187 Bool ancestorShaped
, /* ancestor was a Shaped window */
188 Region ancestorRegion
/* parent rel. effective Bounding region of ancestors */
192 XWindowAttributes xwa
;
193 int width
, height
, x1
, y1
;
194 Window root
, parent
, *children
;
197 MVWinVisInfo
*pWinInfo
;
199 Bool isShaped
= False
;
202 unsigned int wb
, hb
, wc
, hc
;
204 Region tmpancestorRegion
, tmpreg
;
205 XRectangle
*rects
, *rect
, tmprct
;
208 if (!XGetWindowAttributes(mvDpy
, win
, &xwa
)
209 || xwa
.map_state
!= IsViewable
210 || xwa
.class == InputOnly
) {
214 /* compute top-left of image in root space */
215 x1
= max(x
, xwa
.x
+px
);
216 y1
= max(y
, xwa
.y
+py
);
217 width
= min(x
+wi
, xwa
.x
+xwa
.width
+2*xwa
.border_width
+px
)-x1
;
218 height
=min(y
+hi
, xwa
.y
+xwa
.height
+2*xwa
.border_width
+py
)-y1
;
220 if (width
<=0 || height
<= 0) {
224 /* We're interested ... */
226 if (winList
.used
>= winList
.allocated
) { /* expand or create the array */
227 winList
.allocated
= (winList
.allocated
?winList
.allocated
*MV_WIN_TUNE2
:
229 winList
.wins
= (MVWinVisInfo
*)(winList
.wins
?
230 realloc(winList
.wins
,winList
.allocated
*sizeof(MVWinVisInfo
)):
231 malloc(winList
.allocated
*sizeof(MVWinVisInfo
)));
234 pWinInfo
= &(winList
.wins
[winList
.used
++]);
235 pWinInfo
->window
= win
;
236 pWinInfo
->depth
= xwa
.depth
;
237 pWinInfo
->visinfo
= mvMatchVisual(XVisualIDFromVisual(xwa
.visual
));
238 pWinInfo
->colmap
= mvFindColormap(xwa
.colormap
);
239 pWinInfo
->x
= x1
-xwa
.border_width
-xwa
.x
-px
;
240 pWinInfo
->y
= y1
-xwa
.border_width
-xwa
.y
-py
;
241 pWinInfo
->width
= width
;
242 pWinInfo
->height
= height
;
246 pWinInfo
->region
= NULL
;
247 /* Is it a Shaped Window ? Help ! */
248 if ((XShapeQueryExtents(mvDpy
, win
, &bdg
, &xb
, &yb
, &wb
, &hb
,
249 &clp
, &xc
, &yc
, &wc
, &hc
)) && bdg
) {
251 if ((wb
== 0) || (hb
== 0) || (wb
> 2000) || (hb
> 2000)) {
252 /* Empty shape, ignore it */
258 pWinInfo
->region
= XCreateRegion();
260 XShapeGetRectangles(mvDpy
, win
, ShapeBounding
, &count
, &ordering
);
263 XUnionRectWithRegion(rect
++, pWinInfo
->region
, pWinInfo
->region
);
266 XFree((caddr_t
)rects
);
269 tmpreg
= XCreateRegion();
270 tmprct
.x
= pWinInfo
->x
; tmprct
.y
= pWinInfo
->y
;
271 tmprct
.width
= pWinInfo
->width
; tmprct
.height
= pWinInfo
->height
;
272 XUnionRectWithRegion(&tmprct
, tmpreg
, tmpreg
);
273 XIntersectRegion(pWinInfo
->region
, tmpreg
, pWinInfo
->region
);
274 XDestroyRegion(tmpreg
);
276 if (ancestorShaped
) {
277 /* make a local copy */
278 tmpancestorRegion
= XCreateRegion();
279 XUnionRegion(tmpancestorRegion
, ancestorRegion
, tmpancestorRegion
);
280 /* Translate from relative parent's origin to this window's origin */
281 XOffsetRegion(tmpancestorRegion
, -(xwa
.x
+ xwa
.border_width
),
282 -(xwa
.y
+ xwa
.border_width
));
283 if (isShaped
) { /* Compute effective bdg shape */
284 XIntersectRegion(tmpancestorRegion
, pWinInfo
->region
, pWinInfo
->region
);
287 pWinInfo
->region
= XCreateRegion();
288 XUnionRegion(tmpancestorRegion
, pWinInfo
->region
, pWinInfo
->region
);
289 tmpreg
= XCreateRegion();
290 tmprct
.x
= pWinInfo
->x
; tmprct
.y
= pWinInfo
->y
;
291 tmprct
.width
= pWinInfo
->width
; tmprct
.height
= pWinInfo
->height
;
292 XUnionRectWithRegion(&tmprct
, tmpreg
, tmpreg
);
293 XIntersectRegion(pWinInfo
->region
, tmpreg
, pWinInfo
->region
);
294 XDestroyRegion(tmpreg
);
296 XDestroyRegion(tmpancestorRegion
);
300 /* Find children, back to front */
301 if (XQueryTree(mvDpy
, win
, &root
, &parent
, &children
, &nchild
)) {
304 tmpreg
= pWinInfo
->region
;
307 for (n
=0; n
<nchild
; n
++) {
309 mvWalkTree(children
[n
], px
+xwa
.x
+xwa
.border_width
,
310 py
+xwa
.y
+xwa
.border_width
,
311 x1
, y1
, width
, height
, (ancestorShaped
|| isShaped
),
314 mvWalkTree(children
[n
], px
+xwa
.x
+xwa
.border_width
,
315 py
+xwa
.y
+xwa
.border_width
,
316 x1
, y1
, width
, height
);
321 XFree((caddr_t
)children
);
328 * Returns 0 if no problems,
329 * Returns 1 if depths differ
330 * returns 2 if colormap or visinfo differ
331 * NOTE that this chenge & the previous change are reprehensible hacks,
332 * to let xmag work with pageview, and xcolor respectively.
335 * Returns 0 if no problems,
336 * Returns 1 if visinfo or depth differ
337 * returns 2 if colormap only differ
339 * Returns non-zero if the winList created by mvWalkTree
340 * might potentially have windows of different Visuals
347 int i
= winList
.used
;
350 if(winList
.wins
[i
].depth
!= winList
.wins
[0].depth
)
352 if (winList
.wins
[i
].visinfo
!= winList
.wins
[0].visinfo
)
354 if (winList
.wins
[i
].colmap
!= winList
.wins
[0].colmap
)
361 * Traverse the window list front to back. Get the entire Image
362 * from each window, but only Label a pixel in the Img once.
363 * That is, once a pixel has been Labeled, any more fetches
364 * from the same pixel position are discarded. Once all pixels
365 * positions have been fetched, we're done. This will eliminate
366 * windows that have nothing to contibute to the requested region,
367 * but will nevertheless have the problem of the painters
368 * algorithm, where more pixels were fetched from a
369 * window than were essential.
370 * Assumes that winList has been filled beforehand, and Img was cleared.
374 mvDoWindowsFrontToBack(void)
380 int nPixelsUnLabeled
= request_width
*request_height
;
382 for (i
=winList
.used
-1; ((nPixelsUnLabeled
> 0) && i
>= 0); i
--) {
383 pWI
= &(winList
.wins
[i
]);
384 if (!(xim
= XGetImage(mvDpy
, pWI
->window
, pWI
->x
, pWI
->y
, pWI
->width
,
385 pWI
->height
, (~0), ZPixmap
))) {
389 /* For each pixel in the returned Image */
390 for (yi
= 0; yi
< pWI
->height
; yi
++) {
392 if ((yi
% 128) == 0) {
393 (*mvCallbackFunction
)(mvCallbackData
);
395 #endif /* UPDATE_HACK */
396 for (xi
= 0; xi
< pWI
->width
; xi
++) {
397 MVPel
*pPel
= mvFindPel(xi
+pWI
->x1
-request_x
, yi
+pWI
->y1
-request_y
);
398 /* If the pixel hasn't been labelled before */
399 if (!pPel
->colmap
) { /* pPel->colmap serves as a 'Label' */
400 /* label the pixel in the map with this window's cmap */
402 * If Pixel value can be discarded, this is where
403 * you get the RGB value instead.
404 * Call a routine like mvFindColorInColormap() with the pixel
405 * value, and Colormap as parameters.
406 * The 'Label', instead of pPel->colmap could be a scratch bit ?
407 * But if its a full 32 bit pixel, and there are no
408 * free bits, you need to hang in extra bits somewhere.
409 * Maybe a bitmask associated with Img ?
412 if (!(pWI
->region
) ||
413 (pWI
->region
&& (XPointInRegion(pWI
->region
, xi
+pWI
->x
, yi
+pWI
->y
))
415 && (XPointInRegion(pWI
->region
, xi
+pWI
->x
+1, yi
+pWI
->y
))
416 && (XPointInRegion(pWI
->region
, xi
+pWI
->x
, yi
+pWI
->y
+1))
417 && (XPointInRegion(pWI
->region
, xi
+pWI
->x
+1, yi
+pWI
->y
+1))
422 pPel
->colmap
= pWI
->colmap
;
423 /* and pixel value */
424 pPel
->pixel
= XGetPixel(xim
, xi
, yi
);
438 * Get all the colors from this window's colormap.
439 * That's slightly complicated when we hit
440 * a true color or direct color visual.
441 * Assumes that a global list of colmaps is present, and
442 * that the Colors field was NULLed beforehand.
446 mvGetColormap(MVWinVisInfo
*pWI
)
448 if (!pWI
->colmap
->Colors
) { /* This is the first time we're visiting */
449 MVColmap
*pCmp
= pWI
->colmap
;
450 XVisualInfo
*pVis
= pWI
->visinfo
;
452 int size
= pVis
->colormap_size
;
454 /* Allocate enough memory */
455 pCmp
->Colors
= pCol
= (XColor
*)calloc((sizeof(XColor
)), size
);
456 if (pVis
->class == TrueColor
|| pVis
->class == DirectColor
) {
458 /* We have to create a composite pixel value */
459 mvCalculateComposite(pWI
);
460 for (i
= 0; i
< (unsigned long)(size
);i
++, pCol
++) {
461 pCol
->pixel
= mvCompositePixel(i
, pCmp
);
466 /* Fill in the pixel values by hand */
467 for (i
= 0; i
< (unsigned long)(size
);) {
471 XQueryColors(mvDpy
, pCmp
->cmap
, pCmp
->Colors
, size
);
476 * Given a VisualID, return a pointer to the VisualInfo structure.
477 * Assumes that a global mvVlist for this screen has already
478 * been created. Uses globals mvNumVis, and mvVlist.
479 * Returns NULL if the vid is not matched.
482 mvMatchVisual(VisualID vid
)
484 XVisualInfo
*pVis
= mvVlist
;
485 while (pVis
< (mvVlist
+mvNumVis
)) {
486 if (vid
== pVis
->visualid
) {
495 * Calculate a composite pixel value that indexes into all
496 * three primaries . Assumes Composite Calcs have been performed
497 * already on the colmap.
500 mvCompositePixel(unsigned long i
, MVColmap
*pCmp
)
502 unsigned long val
= 0;
504 if (i
< pCmp
->rmax
) val
|= i
<< pCmp
->rshft
;
505 if (i
< pCmp
->gmax
) val
|= i
<< pCmp
->gshft
;
506 if (i
< pCmp
->bmax
) val
|= i
<< pCmp
->bshft
;
511 * Calculate the offsets used to composite a pixel value for
512 * the TrueColor & DirectColor cases
513 * Assumes its called only on a True or DirectColor visual.
516 mvCalculateComposite(MVWinVisInfo
*pWI
)
518 MVColmap
*pCmp
= pWI
->colmap
;
519 XVisualInfo
*pVis
= pWI
->visinfo
;
520 /* Check if this has been done before */
521 if (!pCmp
->doComposite
) {
522 pCmp
->doComposite
= True
;
523 /* These are the sizes of each primary map ... */
524 pCmp
->red_mask
= pVis
->red_mask
;
525 pCmp
->green_mask
= pVis
->green_mask
;
526 pCmp
->blue_mask
= pVis
->blue_mask
;
527 pCmp
->rmax
= 1 << mvOnes(pVis
->red_mask
);
528 pCmp
->gmax
= 1 << mvOnes(pVis
->green_mask
);
529 pCmp
->bmax
= 1 << mvOnes(pVis
->blue_mask
);
530 pCmp
->rshft
= mvShifts(pVis
->red_mask
);
531 pCmp
->gshft
= mvShifts(pVis
->green_mask
);
532 pCmp
->bshft
= mvShifts(pVis
->blue_mask
);
533 pCmp
->rgbshft
= (16 - pVis
->bits_per_rgb
);
538 * Calculate number of 1 bits in mask
539 * Classic hack not written by this author.
542 mvOnes(unsigned long mask
)
546 y
= (mask
>> 1) &033333333333;
547 y
= mask
- y
- ((y
>> 1) & 033333333333);
548 return (((y
+ (y
>> 3)) & 030707070707) % 077);
552 * Calculate the number of shifts till we hit the mask
555 mvShifts(unsigned long mask
)
569 * find & creat a colmap struct for this cmap
570 * Assumes that colMaps was cleared before the first time
574 mvFindColormap(Colormap cmap
)
577 /* if we've seen this cmap before, return its struct colmap */
578 for (pCmap
= colMaps
; pCmap
; pCmap
= pCmap
->next
) {
579 if (cmap
== pCmap
->cmap
)
582 /* First time for this cmap, creat & link */
583 pCmap
= (MVColmap
*) calloc(sizeof(MVColmap
), 1);
584 pCmap
->next
= colMaps
;
591 * Use pixel value at x, y as an index into
592 * the colmap's list of Colors.
593 * If the pixel value were not important, this would be called
594 * in mvDoWindowsFrontToBack(), with the pixel value
595 * and colmap as parameters, to get RGB values directly.
598 mvFindColorInColormap(int x
, int y
)
600 MVPel
*pPel
= mvFindPel(x
, y
);
601 MVColmap
*pCmap
= pPel
->colmap
;
602 static XColor scratch
;
604 if (pCmap
->doComposite
) { /* This is either True or DirectColor */
605 /* Treat the pixel value as 3 separate indices, composite
606 the color into scratch, return a pointer to scratch */
607 unsigned long pix
= pPel
->pixel
;
608 unsigned long index
= (pix
& pCmap
->red_mask
) >> pCmap
->rshft
;
609 scratch
.red
=pCmap
->Colors
[(pix
& pCmap
->red_mask
)>>pCmap
->rshft
].red
;
610 scratch
.green
=pCmap
->Colors
[(pix
& pCmap
->green_mask
)>>pCmap
->gshft
].green
;
611 scratch
.blue
=pCmap
->Colors
[(pix
& pCmap
->blue_mask
)>>pCmap
->bshft
].blue
;
615 else { /* This is simple */
616 return &(pCmap
->Colors
[pPel
->pixel
]);