rootlessScreen.c revision 05b261ec
1/*
2 * Screen routines for generic rootless X server
3 */
4/*
5 * Copyright (c) 2001 Greg Parker. All Rights Reserved.
6 * Copyright (c) 2002-2003 Torrey T. Lyons. All Rights Reserved.
7 * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
8 *
9 * Permission is hereby granted, free of charge, to any person obtaining a
10 * copy of this software and associated documentation files (the "Software"),
11 * to deal in the Software without restriction, including without limitation
12 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 * and/or sell copies of the Software, and to permit persons to whom the
14 * Software is furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
22 * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
23 * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 * DEALINGS IN THE SOFTWARE.
26 *
27 * Except as contained in this notice, the name(s) of the above copyright
28 * holders shall not be used in advertising or otherwise to promote the sale,
29 * use or other dealings in this Software without prior written authorization.
30 */
31
32
33#ifdef HAVE_DIX_CONFIG_H
34#include <dix-config.h>
35#endif
36
37#include "mi.h"
38#include "scrnintstr.h"
39#include "gcstruct.h"
40#include "pixmapstr.h"
41#include "windowstr.h"
42#include "propertyst.h"
43#include "mivalidate.h"
44#include "picturestr.h"
45
46#include <sys/types.h>
47#include <sys/stat.h>
48#include <fcntl.h>
49#include <string.h>
50
51#include "rootlessCommon.h"
52#include "rootlessWindow.h"
53
54/* In milliseconds */
55#ifndef ROOTLESS_REDISPLAY_DELAY
56#define ROOTLESS_REDISPLAY_DELAY 10
57#endif
58
59extern int RootlessMiValidateTree(WindowPtr pRoot, WindowPtr pChild,
60                                  VTKind kind);
61extern Bool RootlessCreateGC(GCPtr pGC);
62
63// Initialize globals
64int rootlessGCPrivateIndex = -1;
65int rootlessScreenPrivateIndex = -1;
66int rootlessWindowPrivateIndex = -1;
67
68
69/*
70 * RootlessUpdateScreenPixmap
71 *  miCreateScreenResources does not like a null framebuffer pointer,
72 *  it leaves the screen pixmap with an uninitialized data pointer.
73 *  Thus, rootless implementations typically set the framebuffer width
74 *  to zero so that miCreateScreenResources does not allocate a screen
75 *  pixmap for us. We allocate our own screen pixmap here since we need
76 *  the screen pixmap to be valid (e.g. CopyArea from the root window).
77 */
78void
79RootlessUpdateScreenPixmap(ScreenPtr pScreen)
80{
81    RootlessScreenRec *s = SCREENREC(pScreen);
82    PixmapPtr pPix;
83    unsigned int rowbytes;
84
85    pPix = (*pScreen->GetScreenPixmap)(pScreen);
86    if (pPix == NULL) {
87        pPix = (*pScreen->CreatePixmap)(pScreen, 0, 0, pScreen->rootDepth);
88        (*pScreen->SetScreenPixmap)(pPix);
89    }
90
91    rowbytes = PixmapBytePad(pScreen->width, pScreen->rootDepth);
92
93    if (s->pixmap_data_size < rowbytes) {
94        if (s->pixmap_data != NULL)
95            xfree(s->pixmap_data);
96
97        s->pixmap_data_size = rowbytes;
98        s->pixmap_data = xalloc(s->pixmap_data_size);
99        if (s->pixmap_data == NULL)
100            return;
101
102        memset(s->pixmap_data, 0xFF, s->pixmap_data_size);
103
104        pScreen->ModifyPixmapHeader(pPix, pScreen->width, pScreen->height,
105                                    pScreen->rootDepth,
106                                    BitsPerPixel(pScreen->rootDepth),
107                                    0, s->pixmap_data);
108        /* ModifyPixmapHeader ignores zero arguments, so install rowbytes
109           by hand. */
110        pPix->devKind = 0;
111    }
112}
113
114
115/*
116 * RootlessCreateScreenResources
117 *  Rootless implementations typically set a null framebuffer pointer, which
118 *  causes problems with miCreateScreenResources. We fix things up here.
119 */
120static Bool
121RootlessCreateScreenResources(ScreenPtr pScreen)
122{
123    Bool ret = TRUE;
124
125    SCREEN_UNWRAP(pScreen, CreateScreenResources);
126
127    if (pScreen->CreateScreenResources != NULL)
128        ret = (*pScreen->CreateScreenResources)(pScreen);
129
130    SCREEN_WRAP(pScreen, CreateScreenResources);
131
132    if (!ret)
133        return ret;
134
135    /* Make sure we have a valid screen pixmap. */
136
137    RootlessUpdateScreenPixmap(pScreen);
138
139    return ret;
140}
141
142
143static Bool
144RootlessCloseScreen(int i, ScreenPtr pScreen)
145{
146    RootlessScreenRec *s;
147
148    s = SCREENREC(pScreen);
149
150    // fixme unwrap everything that was wrapped?
151    pScreen->CloseScreen = s->CloseScreen;
152
153    if (s->pixmap_data != NULL) {
154        xfree (s->pixmap_data);
155        s->pixmap_data = NULL;
156        s->pixmap_data_size = 0;
157    }
158
159    xfree(s);
160    return pScreen->CloseScreen(i, pScreen);
161}
162
163
164static void
165RootlessGetImage(DrawablePtr pDrawable, int sx, int sy, int w, int h,
166                 unsigned int format, unsigned long planeMask, char *pdstLine)
167{
168    ScreenPtr pScreen = pDrawable->pScreen;
169    SCREEN_UNWRAP(pScreen, GetImage);
170
171    if (pDrawable->type == DRAWABLE_WINDOW) {
172        int x0, y0, x1, y1;
173        RootlessWindowRec *winRec;
174
175        // Many apps use GetImage to sync with the visible frame buffer
176        // FIXME: entire screen or just window or all screens?
177        RootlessRedisplayScreen(pScreen);
178
179        // RedisplayScreen stops drawing, so we need to start it again
180        RootlessStartDrawing((WindowPtr)pDrawable);
181
182        /* Check that we have some place to read from. */
183        winRec = WINREC(TopLevelParent((WindowPtr) pDrawable));
184        if (winRec == NULL)
185            goto out;
186
187        /* Clip to top-level window bounds. */
188        /* FIXME: fbGetImage uses the width parameter to calculate the
189           stride of the destination pixmap. If w is clipped, the data
190           returned will be garbage, although we will not crash. */
191
192        x0 = pDrawable->x + sx;
193        y0 = pDrawable->y + sy;
194        x1 = x0 + w;
195        y1 = y0 + h;
196
197        x0 = MAX (x0, winRec->x);
198        y0 = MAX (y0, winRec->y);
199        x1 = MIN (x1, winRec->x + winRec->width);
200        y1 = MIN (y1, winRec->y + winRec->height);
201
202        sx = x0 - pDrawable->x;
203        sy = y0 - pDrawable->y;
204        w = x1 - x0;
205        h = y1 - y0;
206
207        if (w <= 0 || h <= 0)
208            goto out;
209    }
210
211    pScreen->GetImage(pDrawable, sx, sy, w, h, format, planeMask, pdstLine);
212
213out:
214    SCREEN_WRAP(pScreen, GetImage);
215}
216
217
218/*
219 * RootlessSourceValidate
220 *  CopyArea and CopyPlane use a GC tied to the destination drawable.
221 *  StartDrawing/StopDrawing wrappers won't be called if source is
222 *  a visible window but the destination isn't. So, we call StartDrawing
223 *  here and leave StopDrawing for the block handler.
224 */
225static void
226RootlessSourceValidate(DrawablePtr pDrawable, int x, int y, int w, int h)
227{
228    SCREEN_UNWRAP(pDrawable->pScreen, SourceValidate);
229    if (pDrawable->type == DRAWABLE_WINDOW) {
230        WindowPtr pWin = (WindowPtr)pDrawable;
231        RootlessStartDrawing(pWin);
232    }
233    if (pDrawable->pScreen->SourceValidate) {
234        pDrawable->pScreen->SourceValidate(pDrawable, x, y, w, h);
235    }
236    SCREEN_WRAP(pDrawable->pScreen, SourceValidate);
237}
238
239#ifdef RENDER
240
241static void
242RootlessComposite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst,
243                  INT16 xSrc, INT16 ySrc, INT16  xMask, INT16  yMask,
244                  INT16 xDst, INT16 yDst, CARD16 width, CARD16 height)
245{
246    ScreenPtr pScreen = pDst->pDrawable->pScreen;
247    PictureScreenPtr ps = GetPictureScreen(pScreen);
248    WindowPtr srcWin, dstWin, maskWin = NULL;
249
250    if (pMask) {                        // pMask can be NULL
251        maskWin = (pMask->pDrawable->type == DRAWABLE_WINDOW) ?
252                  (WindowPtr)pMask->pDrawable :  NULL;
253    }
254    srcWin  = (pSrc->pDrawable->type  == DRAWABLE_WINDOW) ?
255              (WindowPtr)pSrc->pDrawable  :  NULL;
256    dstWin  = (pDst->pDrawable->type == DRAWABLE_WINDOW) ?
257              (WindowPtr)pDst->pDrawable  :  NULL;
258
259    // SCREEN_UNWRAP(ps, Composite);
260    ps->Composite = SCREENREC(pScreen)->Composite;
261
262    if (srcWin  && IsFramedWindow(srcWin))
263        RootlessStartDrawing(srcWin);
264    if (maskWin && IsFramedWindow(maskWin))
265        RootlessStartDrawing(maskWin);
266    if (dstWin  && IsFramedWindow(dstWin))
267        RootlessStartDrawing(dstWin);
268
269    ps->Composite(op, pSrc, pMask, pDst,
270                  xSrc, ySrc, xMask, yMask,
271                  xDst, yDst, width, height);
272
273    if (dstWin  && IsFramedWindow(dstWin)) {
274        RootlessDamageRect(dstWin, xDst, yDst, width, height);
275    }
276
277    ps->Composite = RootlessComposite;
278    // SCREEN_WRAP(ps, Composite);
279}
280
281
282static void
283RootlessGlyphs(CARD8 op, PicturePtr pSrc, PicturePtr pDst,
284               PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc,
285               int nlist, GlyphListPtr list, GlyphPtr *glyphs)
286{
287    ScreenPtr pScreen = pDst->pDrawable->pScreen;
288    PictureScreenPtr ps = GetPictureScreen(pScreen);
289    int x, y;
290    int n;
291    GlyphPtr glyph;
292    WindowPtr srcWin, dstWin;
293
294    srcWin = (pSrc->pDrawable->type == DRAWABLE_WINDOW) ?
295             (WindowPtr)pSrc->pDrawable  :  NULL;
296    dstWin = (pDst->pDrawable->type == DRAWABLE_WINDOW) ?
297             (WindowPtr)pDst->pDrawable  :  NULL;
298
299    if (srcWin && IsFramedWindow(srcWin)) RootlessStartDrawing(srcWin);
300    if (dstWin && IsFramedWindow(dstWin)) RootlessStartDrawing(dstWin);
301
302    //SCREEN_UNWRAP(ps, Glyphs);
303    ps->Glyphs = SCREENREC(pScreen)->Glyphs;
304    ps->Glyphs(op, pSrc, pDst, maskFormat, xSrc, ySrc, nlist, list, glyphs);
305    ps->Glyphs = RootlessGlyphs;
306    //SCREEN_WRAP(ps, Glyphs);
307
308    if (dstWin && IsFramedWindow(dstWin)) {
309        x = xSrc;
310        y = ySrc;
311
312        while (nlist--) {
313            x += list->xOff;
314            y += list->yOff;
315            n = list->len;
316
317            /* Calling DamageRect for the bounding box of each glyph is
318               inefficient. So compute the union of all glyphs in a list
319               and damage that. */
320
321            if (n > 0) {
322                BoxRec box;
323
324                glyph = *glyphs++;
325
326                box.x1 = x - glyph->info.x;
327                box.y1 = y - glyph->info.y;
328                box.x2 = box.x1 + glyph->info.width;
329                box.y2 = box.y2 + glyph->info.height;
330
331                x += glyph->info.xOff;
332                y += glyph->info.yOff;
333
334                while (--n > 0) {
335                    short x1, y1, x2, y2;
336
337                    glyph = *glyphs++;
338
339                    x1 = x - glyph->info.x;
340                    y1 = y - glyph->info.y;
341                    x2 = x1 + glyph->info.width;
342                    y2 = y1 + glyph->info.height;
343
344                    box.x1 = MAX (box.x1, x1);
345                    box.y1 = MAX (box.y1, y1);
346                    box.x2 = MAX (box.x2, x2);
347                    box.y2 = MAX (box.y2, y2);
348
349                    x += glyph->info.xOff;
350                    y += glyph->info.yOff;
351                }
352
353                RootlessDamageBox(dstWin, &box);
354            }
355            list++;
356        }
357    }
358}
359
360#endif // RENDER
361
362
363/*
364 * RootlessValidateTree
365 *  ValidateTree is modified in two ways:
366 *   - top-level windows don't clip each other
367 *   - windows aren't clipped against root.
368 *  These only matter when validating from the root.
369 */
370static int
371RootlessValidateTree(WindowPtr pParent, WindowPtr pChild, VTKind kind)
372{
373    int result;
374    RegionRec saveRoot;
375    ScreenPtr pScreen = pParent->drawable.pScreen;
376
377    SCREEN_UNWRAP(pScreen, ValidateTree);
378    RL_DEBUG_MSG("VALIDATETREE start ");
379
380    // Use our custom version to validate from root
381    if (IsRoot(pParent)) {
382        RL_DEBUG_MSG("custom ");
383        result = RootlessMiValidateTree(pParent, pChild, kind);
384    } else {
385        HUGE_ROOT(pParent);
386        result = pScreen->ValidateTree(pParent, pChild, kind);
387        NORMAL_ROOT(pParent);
388    }
389
390    SCREEN_WRAP(pScreen, ValidateTree);
391    RL_DEBUG_MSG("VALIDATETREE end\n");
392
393    return result;
394}
395
396
397/*
398 * RootlessMarkOverlappedWindows
399 *  MarkOverlappedWindows is modified to ignore overlapping
400 *  top-level windows.
401 */
402static Bool
403RootlessMarkOverlappedWindows(WindowPtr pWin, WindowPtr pFirst,
404                              WindowPtr *ppLayerWin)
405{
406    RegionRec saveRoot;
407    Bool result;
408    ScreenPtr pScreen = pWin->drawable.pScreen;
409    SCREEN_UNWRAP(pScreen, MarkOverlappedWindows);
410    RL_DEBUG_MSG("MARKOVERLAPPEDWINDOWS start ");
411
412    HUGE_ROOT(pWin);
413    if (IsRoot(pWin)) {
414        // root - mark nothing
415        RL_DEBUG_MSG("is root not marking ");
416        result = FALSE;
417    }
418    else if (! IsTopLevel(pWin)) {
419        // not top-level window - mark normally
420        result = pScreen->MarkOverlappedWindows(pWin, pFirst, ppLayerWin);
421    }
422    else {
423        //top-level window - mark children ONLY - NO overlaps with sibs (?)
424        // This code copied from miMarkOverlappedWindows()
425
426        register WindowPtr pChild;
427        Bool anyMarked = FALSE;
428        void (* MarkWindow)() = pScreen->MarkWindow;
429
430        RL_DEBUG_MSG("is top level! ");
431        /* single layered systems are easy */
432        if (ppLayerWin) *ppLayerWin = pWin;
433
434        if (pWin == pFirst) {
435            /* Blindly mark pWin and all of its inferiors.   This is a slight
436             * overkill if there are mapped windows that outside pWin's border,
437             * but it's better than wasting time on RectIn checks.
438             */
439            pChild = pWin;
440            while (1) {
441                if (pChild->viewable) {
442                    if (REGION_BROKEN (pScreen, &pChild->winSize))
443                        SetWinSize (pChild);
444                    if (REGION_BROKEN (pScreen, &pChild->borderSize))
445                        SetBorderSize (pChild);
446                    (* MarkWindow)(pChild);
447                    if (pChild->firstChild) {
448                        pChild = pChild->firstChild;
449                        continue;
450                    }
451                }
452                while (!pChild->nextSib && (pChild != pWin))
453                    pChild = pChild->parent;
454                if (pChild == pWin)
455                    break;
456                pChild = pChild->nextSib;
457            }
458            anyMarked = TRUE;
459            pFirst = pFirst->nextSib;
460        }
461        if (anyMarked)
462            (* MarkWindow)(pWin->parent);
463        result = anyMarked;
464    }
465    NORMAL_ROOT(pWin);
466    SCREEN_WRAP(pScreen, MarkOverlappedWindows);
467    RL_DEBUG_MSG("MARKOVERLAPPEDWINDOWS end\n");
468
469    return result;
470}
471
472
473static CARD32
474RootlessRedisplayCallback(OsTimerPtr timer, CARD32 time, void *arg)
475{
476    RootlessScreenRec *screenRec = arg;
477
478    if (!screenRec->redisplay_queued) {
479        /* No update needed. Stop the timer. */
480
481        screenRec->redisplay_timer_set = FALSE;
482        return 0;
483    }
484
485    screenRec->redisplay_queued = FALSE;
486
487    /* Mark that we should redisplay before waiting for I/O next time */
488    screenRec->redisplay_expired = TRUE;
489
490    /* Reinstall the timer immediately, so we get as close to our
491       redisplay interval as possible. */
492
493    return ROOTLESS_REDISPLAY_DELAY;
494}
495
496
497/*
498 * RootlessQueueRedisplay
499 *  Queue a redisplay after a timer delay to ensure we do not redisplay
500 *  too frequently.
501 */
502void
503RootlessQueueRedisplay(ScreenPtr pScreen)
504{
505    RootlessScreenRec *screenRec = SCREENREC(pScreen);
506
507    screenRec->redisplay_queued = TRUE;
508
509    if (screenRec->redisplay_timer_set)
510        return;
511
512    screenRec->redisplay_timer = TimerSet(screenRec->redisplay_timer,
513                                          0, ROOTLESS_REDISPLAY_DELAY,
514                                          RootlessRedisplayCallback,
515                                          screenRec);
516    screenRec->redisplay_timer_set = TRUE;
517}
518
519
520/*
521 * RootlessBlockHandler
522 *  If the redisplay timer has expired, flush drawing before blocking
523 *  on select().
524 */
525static void
526RootlessBlockHandler(pointer pbdata, OSTimePtr pTimeout, pointer pReadmask)
527{
528    ScreenPtr pScreen = pbdata;
529    RootlessScreenRec *screenRec = SCREENREC(pScreen);
530
531    if (screenRec->redisplay_expired) {
532        screenRec->redisplay_expired = FALSE;
533
534        RootlessRedisplayScreen(pScreen);
535    }
536}
537
538
539static void
540RootlessWakeupHandler(pointer data, int i, pointer LastSelectMask)
541{
542    // nothing here
543}
544
545
546static Bool
547RootlessAllocatePrivates(ScreenPtr pScreen)
548{
549    RootlessScreenRec *s;
550    static unsigned long rootlessGeneration = 0;
551
552    if (rootlessGeneration != serverGeneration) {
553        rootlessScreenPrivateIndex = AllocateScreenPrivateIndex();
554        if (rootlessScreenPrivateIndex == -1) return FALSE;
555        rootlessGCPrivateIndex = AllocateGCPrivateIndex();
556        if (rootlessGCPrivateIndex == -1) return FALSE;
557        rootlessWindowPrivateIndex = AllocateWindowPrivateIndex();
558        if (rootlessWindowPrivateIndex == -1) return FALSE;
559        rootlessGeneration = serverGeneration;
560    }
561
562    // no allocation needed for screen privates
563    if (!AllocateGCPrivate(pScreen, rootlessGCPrivateIndex,
564                           sizeof(RootlessGCRec)))
565        return FALSE;
566    if (!AllocateWindowPrivate(pScreen, rootlessWindowPrivateIndex, 0))
567        return FALSE;
568
569    s = xalloc(sizeof(RootlessScreenRec));
570    if (! s) return FALSE;
571    SCREENREC(pScreen) = s;
572
573    s->pixmap_data = NULL;
574    s->pixmap_data_size = 0;
575
576    s->redisplay_timer = NULL;
577    s->redisplay_timer_set = FALSE;
578
579    return TRUE;
580}
581
582
583static void
584RootlessWrap(ScreenPtr pScreen)
585{
586    RootlessScreenRec *s = (RootlessScreenRec*)
587            pScreen->devPrivates[rootlessScreenPrivateIndex].ptr;
588
589#define WRAP(a) \
590    if (pScreen->a) { \
591        s->a = pScreen->a; \
592    } else { \
593        RL_DEBUG_MSG("null screen fn " #a "\n"); \
594        s->a = NULL; \
595    } \
596    pScreen->a = Rootless##a
597
598    WRAP(CreateScreenResources);
599    WRAP(CloseScreen);
600    WRAP(CreateGC);
601    WRAP(PaintWindowBackground);
602    WRAP(PaintWindowBorder);
603    WRAP(CopyWindow);
604    WRAP(GetImage);
605    WRAP(SourceValidate);
606    WRAP(CreateWindow);
607    WRAP(DestroyWindow);
608    WRAP(RealizeWindow);
609    WRAP(UnrealizeWindow);
610    WRAP(MoveWindow);
611    WRAP(PositionWindow);
612    WRAP(ResizeWindow);
613    WRAP(RestackWindow);
614    WRAP(ReparentWindow);
615    WRAP(ChangeBorderWidth);
616    WRAP(MarkOverlappedWindows);
617    WRAP(ValidateTree);
618    WRAP(ChangeWindowAttributes);
619
620#ifdef SHAPE
621    WRAP(SetShape);
622#endif
623
624#ifdef RENDER
625    {
626        // Composite and Glyphs don't use normal screen wrapping
627        PictureScreenPtr ps = GetPictureScreen(pScreen);
628        s->Composite = ps->Composite;
629        ps->Composite = RootlessComposite;
630        s->Glyphs = ps->Glyphs;
631        ps->Glyphs = RootlessGlyphs;
632    }
633#endif
634
635    // WRAP(ClearToBackground); fixme put this back? useful for shaped wins?
636    // WRAP(RestoreAreas); fixme put this back?
637
638#undef WRAP
639}
640
641
642/*
643 * RootlessInit
644 *  Called by the rootless implementation to initialize the rootless layer.
645 *  Rootless wraps lots of stuff and needs a bunch of devPrivates.
646 */
647Bool RootlessInit(ScreenPtr pScreen, RootlessFrameProcsPtr procs)
648{
649    RootlessScreenRec *s;
650
651    if (!RootlessAllocatePrivates(pScreen))
652        return FALSE;
653
654    s = (RootlessScreenRec*)
655        pScreen->devPrivates[rootlessScreenPrivateIndex].ptr;
656
657    s->imp = procs;
658
659    RootlessWrap(pScreen);
660
661    if (!RegisterBlockAndWakeupHandlers(RootlessBlockHandler,
662                                        RootlessWakeupHandler,
663                                        (pointer) pScreen))
664    {
665        return FALSE;
666    }
667
668    return TRUE;
669}
670