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