1 /*
2 
3 Copyright 1993, 1998  The Open Group
4 
5 Permission to use, copy, modify, distribute, and sell this software and its
6 documentation for any purpose is hereby granted without fee, provided that
7 the above copyright notice appear in all copies and that both that
8 copyright notice and this permission notice appear in supporting
9 documentation.
10 
11 The above copyright notice and this permission notice shall be included
12 in all copies or substantial portions of the Software.
13 
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 OTHER DEALINGS IN THE SOFTWARE.
21 
22 Except as contained in this notice, the name of The Open Group shall
23 not be used in advertising or otherwise to promote the sale, use or
24 other dealings in this Software without prior written authorization
25 from The Open Group.
26 
27 */
28 
29 #ifdef HAVE_DIX_CONFIG_H
30 #include <dix-config.h>
31 #endif
32 
33 #include <X11/X.h>
34 #include "scrnintstr.h"
35 #include "mi.h"
36 #include "misc.h"
37 #include "os.h"
38 #include "windowstr.h"
39 #include "resource.h"
40 #include "dixstruct.h"
41 #include "gcstruct.h"
42 #include "servermd.h"
43 #include "site.h"
44 #include "X11/extensions/render.h"
45 #include "picturestr.h"
46 #include "randrstr.h"
47 /*
48  *  Scratch pixmap management and device independent pixmap allocation
49  *  function.
50  */
51 
52 /* callable by ddx */
53 PixmapPtr
GetScratchPixmapHeader(ScreenPtr pScreen,int width,int height,int depth,int bitsPerPixel,int devKind,void * pPixData)54 GetScratchPixmapHeader(ScreenPtr pScreen, int width, int height, int depth,
55                        int bitsPerPixel, int devKind, void *pPixData)
56 {
57     PixmapPtr pPixmap = pScreen->pScratchPixmap;
58 
59     if (pPixmap)
60         pScreen->pScratchPixmap = NULL;
61     else
62         /* width and height of 0 means don't allocate any pixmap data */
63         pPixmap = (*pScreen->CreatePixmap) (pScreen, 0, 0, depth, 0);
64 
65     if (pPixmap) {
66         if ((*pScreen->ModifyPixmapHeader) (pPixmap, width, height, depth,
67                                             bitsPerPixel, devKind, pPixData))
68             return pPixmap;
69         (*pScreen->DestroyPixmap) (pPixmap);
70     }
71     return NullPixmap;
72 }
73 
74 /* callable by ddx */
75 void
FreeScratchPixmapHeader(PixmapPtr pPixmap)76 FreeScratchPixmapHeader(PixmapPtr pPixmap)
77 {
78     if (pPixmap) {
79         ScreenPtr pScreen = pPixmap->drawable.pScreen;
80 
81         pPixmap->devPrivate.ptr = NULL; /* lest ddx chases bad ptr */
82         if (pScreen->pScratchPixmap)
83             (*pScreen->DestroyPixmap) (pPixmap);
84         else
85             pScreen->pScratchPixmap = pPixmap;
86     }
87 }
88 
89 Bool
CreateScratchPixmapsForScreen(ScreenPtr pScreen)90 CreateScratchPixmapsForScreen(ScreenPtr pScreen)
91 {
92     unsigned int pixmap_size;
93 
94     pixmap_size = sizeof(PixmapRec) + dixScreenSpecificPrivatesSize(pScreen, PRIVATE_PIXMAP);
95     pScreen->totalPixmapSize =
96         BitmapBytePad(pixmap_size * 8);
97 
98     /* let it be created on first use */
99     pScreen->pScratchPixmap = NULL;
100     return TRUE;
101 }
102 
103 void
FreeScratchPixmapsForScreen(ScreenPtr pScreen)104 FreeScratchPixmapsForScreen(ScreenPtr pScreen)
105 {
106     FreeScratchPixmapHeader(pScreen->pScratchPixmap);
107 }
108 
109 /* callable by ddx */
110 PixmapPtr
AllocatePixmap(ScreenPtr pScreen,int pixDataSize)111 AllocatePixmap(ScreenPtr pScreen, int pixDataSize)
112 {
113     PixmapPtr pPixmap;
114 
115     assert(pScreen->totalPixmapSize > 0);
116 
117     if (pScreen->totalPixmapSize > ((size_t) - 1) - pixDataSize)
118         return NullPixmap;
119 
120     pPixmap = calloc(1, pScreen->totalPixmapSize + pixDataSize);
121     if (!pPixmap)
122         return NullPixmap;
123 
124     dixInitScreenPrivates(pScreen, pPixmap, pPixmap + 1, PRIVATE_PIXMAP);
125     return pPixmap;
126 }
127 
128 /* callable by ddx */
129 void
FreePixmap(PixmapPtr pPixmap)130 FreePixmap(PixmapPtr pPixmap)
131 {
132     dixFiniPrivates(pPixmap, PRIVATE_PIXMAP);
133     free(pPixmap);
134 }
135 
PixmapUnshareSlavePixmap(PixmapPtr slave_pixmap)136 void PixmapUnshareSlavePixmap(PixmapPtr slave_pixmap)
137 {
138      int ihandle = -1;
139      ScreenPtr pScreen = slave_pixmap->drawable.pScreen;
140      pScreen->SetSharedPixmapBacking(slave_pixmap, ((void *)(long)ihandle));
141 }
142 
PixmapShareToSlave(PixmapPtr pixmap,ScreenPtr slave)143 PixmapPtr PixmapShareToSlave(PixmapPtr pixmap, ScreenPtr slave)
144 {
145     PixmapPtr spix;
146     int ret;
147     void *handle;
148     ScreenPtr master = pixmap->drawable.pScreen;
149     int depth = pixmap->drawable.depth;
150 
151     ret = master->SharePixmapBacking(pixmap, slave, &handle);
152     if (ret == FALSE)
153         return NULL;
154 
155     spix = slave->CreatePixmap(slave, 0, 0, depth,
156                                CREATE_PIXMAP_USAGE_SHARED);
157     slave->ModifyPixmapHeader(spix, pixmap->drawable.width,
158                               pixmap->drawable.height, depth, 0,
159                               pixmap->devKind, NULL);
160 
161     /* have the slave pixmap take a reference on the master pixmap
162        later we destroy them both at the same time */
163     pixmap->refcnt++;
164 
165     spix->master_pixmap = pixmap;
166 
167     ret = slave->SetSharedPixmapBacking(spix, handle);
168     if (ret == FALSE) {
169         slave->DestroyPixmap(spix);
170         return NULL;
171     }
172 
173     return spix;
174 }
175 
176 static void
PixmapDirtyDamageDestroy(DamagePtr damage,void * closure)177 PixmapDirtyDamageDestroy(DamagePtr damage, void *closure)
178 {
179     PixmapDirtyUpdatePtr dirty = closure;
180 
181     dirty->damage = NULL;
182 }
183 
184 Bool
PixmapStartDirtyTracking(DrawablePtr src,PixmapPtr slave_dst,int x,int y,int dst_x,int dst_y,Rotation rotation)185 PixmapStartDirtyTracking(DrawablePtr src,
186                          PixmapPtr slave_dst,
187                          int x, int y, int dst_x, int dst_y,
188                          Rotation rotation)
189 {
190     ScreenPtr screen = src->pScreen;
191     PixmapDirtyUpdatePtr dirty_update;
192     RegionPtr damageregion;
193     RegionRec dstregion;
194     BoxRec box;
195 
196     dirty_update = calloc(1, sizeof(PixmapDirtyUpdateRec));
197     if (!dirty_update)
198         return FALSE;
199 
200     dirty_update->src = src;
201     dirty_update->slave_dst = slave_dst;
202     dirty_update->x = x;
203     dirty_update->y = y;
204     dirty_update->dst_x = dst_x;
205     dirty_update->dst_y = dst_y;
206     dirty_update->rotation = rotation;
207     dirty_update->damage = DamageCreate(NULL, PixmapDirtyDamageDestroy,
208                                         DamageReportNone, TRUE, screen,
209                                         dirty_update);
210 
211     if (rotation != RR_Rotate_0) {
212         RRTransformCompute(x, y,
213                            slave_dst->drawable.width,
214                            slave_dst->drawable.height,
215                            rotation,
216                            NULL,
217                            &dirty_update->transform,
218                            &dirty_update->f_transform,
219                            &dirty_update->f_inverse);
220     }
221     if (!dirty_update->damage) {
222         free(dirty_update);
223         return FALSE;
224     }
225 
226     /* Damage destination rectangle so that the destination pixmap contents
227      * will get fully initialized
228      */
229     box.x1 = dirty_update->x;
230     box.y1 = dirty_update->y;
231     if (dirty_update->rotation == RR_Rotate_90 ||
232         dirty_update->rotation == RR_Rotate_270) {
233         box.x2 = dirty_update->x + slave_dst->drawable.height;
234         box.y2 = dirty_update->y + slave_dst->drawable.width;
235     } else {
236         box.x2 = dirty_update->x + slave_dst->drawable.width;
237         box.y2 = dirty_update->y + slave_dst->drawable.height;
238     }
239     RegionInit(&dstregion, &box, 1);
240     damageregion = DamageRegion(dirty_update->damage);
241     RegionUnion(damageregion, damageregion, &dstregion);
242     RegionUninit(&dstregion);
243 
244     DamageRegister(src, dirty_update->damage);
245     xorg_list_add(&dirty_update->ent, &screen->pixmap_dirty_list);
246     return TRUE;
247 }
248 
249 Bool
PixmapStopDirtyTracking(DrawablePtr src,PixmapPtr slave_dst)250 PixmapStopDirtyTracking(DrawablePtr src, PixmapPtr slave_dst)
251 {
252     ScreenPtr screen = src->pScreen;
253     PixmapDirtyUpdatePtr ent, safe;
254 
255     xorg_list_for_each_entry_safe(ent, safe, &screen->pixmap_dirty_list, ent) {
256         if (ent->src == src && ent->slave_dst == slave_dst) {
257             if (ent->damage)
258                 DamageDestroy(ent->damage);
259             xorg_list_del(&ent->ent);
260             free(ent);
261         }
262     }
263     return TRUE;
264 }
265 
266 static void
PixmapDirtyCopyArea(PixmapPtr dst,PixmapDirtyUpdatePtr dirty,RegionPtr dirty_region)267 PixmapDirtyCopyArea(PixmapPtr dst,
268                     PixmapDirtyUpdatePtr dirty,
269                     RegionPtr dirty_region)
270 {
271     DrawablePtr src = dirty->src;
272     ScreenPtr pScreen = src->pScreen;
273     int n;
274     BoxPtr b;
275     GCPtr pGC;
276 
277     n = RegionNumRects(dirty_region);
278     b = RegionRects(dirty_region);
279 
280     pGC = GetScratchGC(src->depth, pScreen);
281     if (pScreen->root) {
282         ChangeGCVal subWindowMode;
283 
284         subWindowMode.val = IncludeInferiors;
285         ChangeGC(NullClient, pGC, GCSubwindowMode, &subWindowMode);
286     }
287     ValidateGC(&dst->drawable, pGC);
288 
289     while (n--) {
290         BoxRec dst_box;
291         int w, h;
292 
293         dst_box = *b;
294         w = dst_box.x2 - dst_box.x1;
295         h = dst_box.y2 - dst_box.y1;
296 
297         pGC->ops->CopyArea(src, &dst->drawable, pGC,
298                            dirty->x + dst_box.x1, dirty->y + dst_box.y1, w, h,
299                            dirty->dst_x + dst_box.x1,
300                            dirty->dst_y + dst_box.y1);
301         b++;
302     }
303     FreeScratchGC(pGC);
304 }
305 
306 static void
PixmapDirtyCompositeRotate(PixmapPtr dst_pixmap,PixmapDirtyUpdatePtr dirty,RegionPtr dirty_region)307 PixmapDirtyCompositeRotate(PixmapPtr dst_pixmap,
308                            PixmapDirtyUpdatePtr dirty,
309                            RegionPtr dirty_region)
310 {
311     ScreenPtr pScreen = dirty->src->pScreen;
312     PictFormatPtr format = PictureWindowFormat(pScreen->root);
313     PicturePtr src, dst;
314     XID include_inferiors = IncludeInferiors;
315     int n = RegionNumRects(dirty_region);
316     BoxPtr b = RegionRects(dirty_region);
317     int error;
318 
319     src = CreatePicture(None,
320                         dirty->src,
321                         format,
322                         CPSubwindowMode,
323                         &include_inferiors, serverClient, &error);
324     if (!src)
325         return;
326 
327     dst = CreatePicture(None,
328                         &dst_pixmap->drawable,
329                         format, 0L, NULL, serverClient, &error);
330     if (!dst)
331         return;
332 
333     error = SetPictureTransform(src, &dirty->transform);
334     if (error)
335         return;
336     while (n--) {
337         BoxRec dst_box;
338 
339         dst_box = *b;
340         dst_box.x1 += dirty->x;
341         dst_box.x2 += dirty->x;
342         dst_box.y1 += dirty->y;
343         dst_box.y2 += dirty->y;
344         pixman_f_transform_bounds(&dirty->f_inverse, &dst_box);
345 
346         CompositePicture(PictOpSrc,
347                          src, NULL, dst,
348                          dst_box.x1,
349                          dst_box.y1,
350                          0, 0,
351                          dst_box.x1,
352                          dst_box.y1,
353                          dst_box.x2 - dst_box.x1,
354                          dst_box.y2 - dst_box.y1);
355         b++;
356     }
357 
358     FreePicture(src, None);
359     FreePicture(dst, None);
360 }
361 
362 /*
363  * this function can possibly be improved and optimised, by clipping
364  * instead of iterating
365  * Drivers are free to implement their own version of this.
366  */
PixmapSyncDirtyHelper(PixmapDirtyUpdatePtr dirty)367 Bool PixmapSyncDirtyHelper(PixmapDirtyUpdatePtr dirty)
368 {
369     ScreenPtr pScreen = dirty->src->pScreen;
370     RegionPtr region = DamageRegion(dirty->damage);
371     PixmapPtr dst;
372     SourceValidateProcPtr SourceValidate;
373     RegionRec pixregion;
374     BoxRec box;
375 
376     dst = dirty->slave_dst->master_pixmap;
377     if (!dst)
378         dst = dirty->slave_dst;
379 
380     box.x1 = 0;
381     box.y1 = 0;
382     if (dirty->rotation == RR_Rotate_90 ||
383         dirty->rotation == RR_Rotate_270) {
384         box.x2 = dst->drawable.height;
385         box.y2 = dst->drawable.width;
386     } else {
387         box.x2 = dst->drawable.width;
388         box.y2 = dst->drawable.height;
389     }
390     RegionInit(&pixregion, &box, 1);
391 
392     /*
393      * SourceValidate is used by the software cursor code
394      * to pull the cursor off of the screen when reading
395      * bits from the frame buffer. Bypassing this function
396      * leaves the software cursor in place
397      */
398     SourceValidate = pScreen->SourceValidate;
399     pScreen->SourceValidate = miSourceValidate;
400 
401     RegionTranslate(&pixregion, dirty->x, dirty->y);
402     RegionIntersect(&pixregion, &pixregion, region);
403 
404     if (RegionNil(&pixregion)) {
405         RegionUninit(&pixregion);
406         return FALSE;
407     }
408 
409     RegionTranslate(&pixregion, -dirty->x, -dirty->y);
410 
411     if (!pScreen->root || dirty->rotation == RR_Rotate_0)
412         PixmapDirtyCopyArea(dst, dirty, &pixregion);
413     else
414         PixmapDirtyCompositeRotate(dst, dirty, &pixregion);
415     pScreen->SourceValidate = SourceValidate;
416     return TRUE;
417 }
418