text: /*
   * Screen routines for generic rootless X server
   */
  /*
   * Copyright (c) 2001 Greg Parker. All Rights Reserved.
   * Copyright (c) 2002-2003 Torrey T. Lyons. All Rights Reserved.
   * Copyright (c) 2002 Apple Computer, Inc. All rights reserved.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a
   * copy of this software and associated documentation files (the "Software"),
   * to deal in the Software without restriction, including without limitation
   * the rights to use, copy, modify, merge, publish, distribute, sublicense,
   * and/or sell copies of the Software, and to permit persons to whom the
   * Software is furnished to do so, subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
   * THE ABOVE LISTED COPYRIGHT HOLDER(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
   * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   * DEALINGS IN THE SOFTWARE.
   *
   * Except as contained in this notice, the name(s) of the above copyright
   * holders shall not be used in advertising or otherwise to promote the sale,
   * use or other dealings in this Software without prior written authorization.
   */
  
  
  #ifdef HAVE_DIX_CONFIG_H
  #include <dix-config.h>
  #endif
  
  #include "mi.h"
  #include "scrnintstr.h"
  #include "gcstruct.h"
  #include "pixmapstr.h"
  #include "windowstr.h"
  #include "propertyst.h"
  #include "mivalidate.h"
  #include "picturestr.h"
  
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <fcntl.h>
  #include <string.h>
  
  #include "rootlessCommon.h"
  #include "rootlessWindow.h"
  
  /* In milliseconds */
  #ifndef ROOTLESS_REDISPLAY_DELAY
  #define ROOTLESS_REDISPLAY_DELAY 10
  #endif
  
  extern int RootlessMiValidateTree(WindowPtr pRoot, WindowPtr pChild,
                                    VTKind kind);
  extern Bool RootlessCreateGC(GCPtr pGC);
  
  // Initialize globals
  int rootlessGCPrivateIndex = -1;
  int rootlessScreenPrivateIndex = -1;
  int rootlessWindowPrivateIndex = -1;
  
  
  /*
   * RootlessUpdateScreenPixmap
   *  miCreateScreenResources does not like a null framebuffer pointer,
   *  it leaves the screen pixmap with an uninitialized data pointer.
   *  Thus, rootless implementations typically set the framebuffer width
   *  to zero so that miCreateScreenResources does not allocate a screen
   *  pixmap for us. We allocate our own screen pixmap here since we need
   *  the screen pixmap to be valid (e.g. CopyArea from the root window).
   */
  void
  RootlessUpdateScreenPixmap(ScreenPtr pScreen)
  {
      RootlessScreenRec *s = SCREENREC(pScreen);
      PixmapPtr pPix;
      unsigned int rowbytes;
  
      pPix = (*pScreen->GetScreenPixmap)(pScreen);
      if (pPix == NULL) {
          pPix = (*pScreen->CreatePixmap)(pScreen, 0, 0, pScreen->rootDepth);
          (*pScreen->SetScreenPixmap)(pPix);
      }
  
      rowbytes = PixmapBytePad(pScreen->width, pScreen->rootDepth);
  
      if (s->pixmap_data_size < rowbytes) {
          if (s->pixmap_data != NULL)
              xfree(s->pixmap_data);
  
          s->pixmap_data_size = rowbytes;
          s->pixmap_data = xalloc(s->pixmap_data_size);
          if (s->pixmap_data == NULL)
              return;
  
          memset(s->pixmap_data, 0xFF, s->pixmap_data_size);
  
          pScreen->ModifyPixmapHeader(pPix, pScreen->width, pScreen->height,
                                      pScreen->rootDepth,
                                      BitsPerPixel(pScreen->rootDepth),
                                      0, s->pixmap_data);
          /* ModifyPixmapHeader ignores zero arguments, so install rowbytes
             by hand. */
          pPix->devKind = 0;
      }
  }
  
  
  /*
   * RootlessCreateScreenResources
   *  Rootless implementations typically set a null framebuffer pointer, which
   *  causes problems with miCreateScreenResources. We fix things up here.
   */
  static Bool
  RootlessCreateScreenResources(ScreenPtr pScreen)
  {
      Bool ret = TRUE;
  
      SCREEN_UNWRAP(pScreen, CreateScreenResources);
  
      if (pScreen->CreateScreenResources != NULL)
          ret = (*pScreen->CreateScreenResources)(pScreen);
  
      SCREEN_WRAP(pScreen, CreateScreenResources);
  
      if (!ret)
          return ret;
  
      /* Make sure we have a valid screen pixmap. */
  
      RootlessUpdateScreenPixmap(pScreen);
  
      return ret;
  }
  
  
  static Bool
  RootlessCloseScreen(int i, ScreenPtr pScreen)
  {
      RootlessScreenRec *s;
  
      s = SCREENREC(pScreen);
  
      // fixme unwrap everything that was wrapped?
      pScreen->CloseScreen = s->CloseScreen;
  
      if (s->pixmap_data != NULL) {
          xfree (s->pixmap_data);
          s->pixmap_data = NULL;
          s->pixmap_data_size = 0;
      }
  
      xfree(s);
      return pScreen->CloseScreen(i, pScreen);
  }
  
  
  static void
  RootlessGetImage(DrawablePtr pDrawable, int sx, int sy, int w, int h,
                   unsigned int format, unsigned long planeMask, char *pdstLine)
  {
      ScreenPtr pScreen = pDrawable->pScreen;
      SCREEN_UNWRAP(pScreen, GetImage);
  
      if (pDrawable->type == DRAWABLE_WINDOW) {
          int x0, y0, x1, y1;
          RootlessWindowRec *winRec;
  
          // Many apps use GetImage to sync with the visible frame buffer
          // FIXME: entire screen or just window or all screens?
          RootlessRedisplayScreen(pScreen);
  
          // RedisplayScreen stops drawing, so we need to start it again
          RootlessStartDrawing((WindowPtr)pDrawable);
  
          /* Check that we have some place to read from. */
          winRec = WINREC(TopLevelParent((WindowPtr) pDrawable));
          if (winRec == NULL)
              goto out;
  
          /* Clip to top-level window bounds. */
          /* FIXME: fbGetImage uses the width parameter to calculate the
             stride of the destination pixmap. If w is clipped, the data
             returned will be garbage, although we will not crash. */
  
          x0 = pDrawable->x + sx;
          y0 = pDrawable->y + sy;
          x1 = x0 + w;
          y1 = y0 + h;
  
          x0 = MAX (x0, winRec->x);
          y0 = MAX (y0, winRec->y);
          x1 = MIN (x1, winRec->x + winRec->width);
          y1 = MIN (y1, winRec->y + winRec->height);
  
          sx = x0 - pDrawable->x;
          sy = y0 - pDrawable->y;
          w = x1 - x0;
          h = y1 - y0;
  
          if (w <= 0 || h <= 0)
              goto out;
      }
  
      pScreen->GetImage(pDrawable, sx, sy, w, h, format, planeMask, pdstLine);
  
  out:
      SCREEN_WRAP(pScreen, GetImage);
  }
  
  
  /*
   * RootlessSourceValidate
   *  CopyArea and CopyPlane use a GC tied to the destination drawable.
   *  StartDrawing/StopDrawing wrappers won't be called if source is
   *  a visible window but the destination isn't. So, we call StartDrawing
   *  here and leave StopDrawing for the block handler.
   */
  static void
  RootlessSourceValidate(DrawablePtr pDrawable, int x, int y, int w, int h)
  {
      SCREEN_UNWRAP(pDrawable->pScreen, SourceValidate);
      if (pDrawable->type == DRAWABLE_WINDOW) {
          WindowPtr pWin = (WindowPtr)pDrawable;
          RootlessStartDrawing(pWin);
      }
      if (pDrawable->pScreen->SourceValidate) {
          pDrawable->pScreen->SourceValidate(pDrawable, x, y, w, h);
      }
      SCREEN_WRAP(pDrawable->pScreen, SourceValidate);
  }
  
  #ifdef RENDER
  
  static void
  RootlessComposite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, PicturePtr pDst,
                    INT16 xSrc, INT16 ySrc, INT16  xMask, INT16  yMask,
                    INT16 xDst, INT16 yDst, CARD16 width, CARD16 height)
  {
      ScreenPtr pScreen = pDst->pDrawable->pScreen;
      PictureScreenPtr ps = GetPictureScreen(pScreen);
      WindowPtr srcWin, dstWin, maskWin = NULL;
  
      if (pMask) {                        // pMask can be NULL
          maskWin = (pMask->pDrawable->type == DRAWABLE_WINDOW) ?
                    (WindowPtr)pMask->pDrawable :  NULL;
      }
      srcWin  = (pSrc->pDrawable->type  == DRAWABLE_WINDOW) ?
                (WindowPtr)pSrc->pDrawable  :  NULL;
      dstWin  = (pDst->pDrawable->type == DRAWABLE_WINDOW) ?
                (WindowPtr)pDst->pDrawable  :  NULL;
  
      // SCREEN_UNWRAP(ps, Composite);
      ps->Composite = SCREENREC(pScreen)->Composite;
  
      if (srcWin  && IsFramedWindow(srcWin))
          RootlessStartDrawing(srcWin);
      if (maskWin && IsFramedWindow(maskWin))
          RootlessStartDrawing(maskWin);
      if (dstWin  && IsFramedWindow(dstWin))
          RootlessStartDrawing(dstWin);
  
      ps->Composite(op, pSrc, pMask, pDst,
                    xSrc, ySrc, xMask, yMask,
                    xDst, yDst, width, height);
  
      if (dstWin  && IsFramedWindow(dstWin)) {
          RootlessDamageRect(dstWin, xDst, yDst, width, height);
      }
  
      ps->Composite = RootlessComposite;
      // SCREEN_WRAP(ps, Composite);
  }
  
  
  static void
  RootlessGlyphs(CARD8 op, PicturePtr pSrc, PicturePtr pDst,
                 PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc,
                 int nlist, GlyphListPtr list, GlyphPtr *glyphs)
  {
      ScreenPtr pScreen = pDst->pDrawable->pScreen;
      PictureScreenPtr ps = GetPictureScreen(pScreen);
      int x, y;
      int n;
      GlyphPtr glyph;
      WindowPtr srcWin, dstWin;
  
      srcWin = (pSrc->pDrawable->type == DRAWABLE_WINDOW) ?
               (WindowPtr)pSrc->pDrawable  :  NULL;
      dstWin = (pDst->pDrawable->type == DRAWABLE_WINDOW) ?
               (WindowPtr)pDst->pDrawable  :  NULL;
  
      if (srcWin && IsFramedWindow(srcWin)) RootlessStartDrawing(srcWin);
      if (dstWin && IsFramedWindow(dstWin)) RootlessStartDrawing(dstWin);
  
      //SCREEN_UNWRAP(ps, Glyphs);
      ps->Glyphs = SCREENREC(pScreen)->Glyphs;
      ps->Glyphs(op, pSrc, pDst, maskFormat, xSrc, ySrc, nlist, list, glyphs);
      ps->Glyphs = RootlessGlyphs;
      //SCREEN_WRAP(ps, Glyphs);
  
      if (dstWin && IsFramedWindow(dstWin)) {
          x = xSrc;
          y = ySrc;
  
          while (nlist--) {
              x += list->xOff;
              y += list->yOff;
              n = list->len;
  
              /* Calling DamageRect for the bounding box of each glyph is
                 inefficient. So compute the union of all glyphs in a list
                 and damage that. */
  
              if (n > 0) {
                  BoxRec box;
  
                  glyph = *glyphs++;
  
                  box.x1 = x - glyph->info.x;
                  box.y1 = y - glyph->info.y;
                  box.x2 = box.x1 + glyph->info.width;
                  box.y2 = box.y2 + glyph->info.height;
  
                  x += glyph->info.xOff;
                  y += glyph->info.yOff;
  
                  while (--n > 0) {
                      short x1, y1, x2, y2;
  
                      glyph = *glyphs++;
  
                      x1 = x - glyph->info.x;
                      y1 = y - glyph->info.y;
                      x2 = x1 + glyph->info.width;
                      y2 = y1 + glyph->info.height;
  
                      box.x1 = MAX (box.x1, x1);
                      box.y1 = MAX (box.y1, y1);
                      box.x2 = MAX (box.x2, x2);
                      box.y2 = MAX (box.y2, y2);
  
                      x += glyph->info.xOff;
                      y += glyph->info.yOff;
                  }
  
                  RootlessDamageBox(dstWin, &box);
              }
              list++;
          }
      }
  }
  
  #endif // RENDER
  
  
  /*
   * RootlessValidateTree
   *  ValidateTree is modified in two ways:
   *   - top-level windows don't clip each other
   *   - windows aren't clipped against root.
   *  These only matter when validating from the root.
   */
  static int
  RootlessValidateTree(WindowPtr pParent, WindowPtr pChild, VTKind kind)
  {
      int result;
      RegionRec saveRoot;
      ScreenPtr pScreen = pParent->drawable.pScreen;
  
      SCREEN_UNWRAP(pScreen, ValidateTree);
      RL_DEBUG_MSG("VALIDATETREE start ");
  
      // Use our custom version to validate from root
      if (IsRoot(pParent)) {
          RL_DEBUG_MSG("custom ");
          result = RootlessMiValidateTree(pParent, pChild, kind);
      } else {
          HUGE_ROOT(pParent);
          result = pScreen->ValidateTree(pParent, pChild, kind);
          NORMAL_ROOT(pParent);
      }
  
      SCREEN_WRAP(pScreen, ValidateTree);
      RL_DEBUG_MSG("VALIDATETREE end\n");
  
      return result;
  }
  
  
  /*
   * RootlessMarkOverlappedWindows
   *  MarkOverlappedWindows is modified to ignore overlapping
   *  top-level windows.
   */
  static Bool
  RootlessMarkOverlappedWindows(WindowPtr pWin, WindowPtr pFirst,
                                WindowPtr *ppLayerWin)
  {
      RegionRec saveRoot;
      Bool result;
      ScreenPtr pScreen = pWin->drawable.pScreen;
      SCREEN_UNWRAP(pScreen, MarkOverlappedWindows);
      RL_DEBUG_MSG("MARKOVERLAPPEDWINDOWS start ");
  
      HUGE_ROOT(pWin);
      if (IsRoot(pWin)) {
          // root - mark nothing
          RL_DEBUG_MSG("is root not marking ");
          result = FALSE;
      }
      else if (! IsTopLevel(pWin)) {
          // not top-level window - mark normally
          result = pScreen->MarkOverlappedWindows(pWin, pFirst, ppLayerWin);
      }
      else {
          //top-level window - mark children ONLY - NO overlaps with sibs (?)
          // This code copied from miMarkOverlappedWindows()
  
          register WindowPtr pChild;
          Bool anyMarked = FALSE;
          void (* MarkWindow)() = pScreen->MarkWindow;
  
          RL_DEBUG_MSG("is top level! ");
          /* single layered systems are easy */
          if (ppLayerWin) *ppLayerWin = pWin;
  
          if (pWin == pFirst) {
              /* Blindly mark pWin and all of its inferiors.   This is a slight
               * overkill if there are mapped windows that outside pWin's border,
               * but it's better than wasting time on RectIn checks.
               */
              pChild = pWin;
              while (1) {
                  if (pChild->viewable) {
                      if (REGION_BROKEN (pScreen, &pChild->winSize))
                          SetWinSize (pChild);
                      if (REGION_BROKEN (pScreen, &pChild->borderSize))
                          SetBorderSize (pChild);
                      (* MarkWindow)(pChild);
                      if (pChild->firstChild) {
                          pChild = pChild->firstChild;
                          continue;
                      }
                  }
                  while (!pChild->nextSib && (pChild != pWin))
                      pChild = pChild->parent;
                  if (pChild == pWin)
                      break;
                  pChild = pChild->nextSib;
              }
              anyMarked = TRUE;
              pFirst = pFirst->nextSib;
          }
          if (anyMarked)
              (* MarkWindow)(pWin->parent);
          result = anyMarked;
      }
      NORMAL_ROOT(pWin);
      SCREEN_WRAP(pScreen, MarkOverlappedWindows);
      RL_DEBUG_MSG("MARKOVERLAPPEDWINDOWS end\n");
  
      return result;
  }
  
  
  static CARD32
  RootlessRedisplayCallback(OsTimerPtr timer, CARD32 time, void *arg)
  {
      RootlessScreenRec *screenRec = arg;
  
      if (!screenRec->redisplay_queued) {
          /* No update needed. Stop the timer. */
  
          screenRec->redisplay_timer_set = FALSE;
          return 0;
      }
  
      screenRec->redisplay_queued = FALSE;
  
      /* Mark that we should redisplay before waiting for I/O next time */
      screenRec->redisplay_expired = TRUE;
  
      /* Reinstall the timer immediately, so we get as close to our
         redisplay interval as possible. */
  
      return ROOTLESS_REDISPLAY_DELAY;
  }
  
  
  /*
   * RootlessQueueRedisplay
   *  Queue a redisplay after a timer delay to ensure we do not redisplay
   *  too frequently.
   */
  void
  RootlessQueueRedisplay(ScreenPtr pScreen)
  {
      RootlessScreenRec *screenRec = SCREENREC(pScreen);
  
      screenRec->redisplay_queued = TRUE;
  
      if (screenRec->redisplay_timer_set)
          return;
  
      screenRec->redisplay_timer = TimerSet(screenRec->redisplay_timer,
                                            0, ROOTLESS_REDISPLAY_DELAY,
                                            RootlessRedisplayCallback,
                                            screenRec);
      screenRec->redisplay_timer_set = TRUE;
  }
  
  
  /*
   * RootlessBlockHandler
   *  If the redisplay timer has expired, flush drawing before blocking
   *  on select().
   */
  static void
  RootlessBlockHandler(pointer pbdata, OSTimePtr pTimeout, pointer pReadmask)
  {
      ScreenPtr pScreen = pbdata;
      RootlessScreenRec *screenRec = SCREENREC(pScreen);
  
      if (screenRec->redisplay_expired) {
          screenRec->redisplay_expired = FALSE;
  
          RootlessRedisplayScreen(pScreen);
      }
  }
  
  
  static void
  RootlessWakeupHandler(pointer data, int i, pointer LastSelectMask)
  {
      // nothing here
  }
  
  
  static Bool
  RootlessAllocatePrivates(ScreenPtr pScreen)
  {
      RootlessScreenRec *s;
      static unsigned long rootlessGeneration = 0;
  
      if (rootlessGeneration != serverGeneration) {
          rootlessScreenPrivateIndex = AllocateScreenPrivateIndex();
          if (rootlessScreenPrivateIndex == -1) return FALSE;
          rootlessGCPrivateIndex = AllocateGCPrivateIndex();
          if (rootlessGCPrivateIndex == -1) return FALSE;
          rootlessWindowPrivateIndex = AllocateWindowPrivateIndex();
          if (rootlessWindowPrivateIndex == -1) return FALSE;
          rootlessGeneration = serverGeneration;
      }
  
      // no allocation needed for screen privates
      if (!AllocateGCPrivate(pScreen, rootlessGCPrivateIndex,
                             sizeof(RootlessGCRec)))
          return FALSE;
      if (!AllocateWindowPrivate(pScreen, rootlessWindowPrivateIndex, 0))
          return FALSE;
  
      s = xalloc(sizeof(RootlessScreenRec));
      if (! s) return FALSE;
      SCREENREC(pScreen) = s;
  
      s->pixmap_data = NULL;
      s->pixmap_data_size = 0;
  
      s->redisplay_timer = NULL;
      s->redisplay_timer_set = FALSE;
  
      return TRUE;
  }
  
  
  static void
  RootlessWrap(ScreenPtr pScreen)
  {
      RootlessScreenRec *s = (RootlessScreenRec*)
              pScreen->devPrivates[rootlessScreenPrivateIndex].ptr;
  
  #define WRAP(a) \
      if (pScreen->a) { \
          s->a = pScreen->a; \
      } else { \
          RL_DEBUG_MSG("null screen fn " #a "\n"); \
          s->a = NULL; \
      } \
      pScreen->a = Rootless##a
  
      WRAP(CreateScreenResources);
      WRAP(CloseScreen);
      WRAP(CreateGC);
      WRAP(PaintWindowBackground);
      WRAP(PaintWindowBorder);
      WRAP(CopyWindow);
      WRAP(GetImage);
      WRAP(SourceValidate);
      WRAP(CreateWindow);
      WRAP(DestroyWindow);
      WRAP(RealizeWindow);
      WRAP(UnrealizeWindow);
      WRAP(MoveWindow);
      WRAP(PositionWindow);
      WRAP(ResizeWindow);
      WRAP(RestackWindow);
      WRAP(ReparentWindow);
      WRAP(ChangeBorderWidth);
      WRAP(MarkOverlappedWindows);
      WRAP(ValidateTree);
      WRAP(ChangeWindowAttributes);
  
  #ifdef SHAPE
      WRAP(SetShape);
  #endif
  
  #ifdef RENDER
      {
          // Composite and Glyphs don't use normal screen wrapping
          PictureScreenPtr ps = GetPictureScreen(pScreen);
          s->Composite = ps->Composite;
          ps->Composite = RootlessComposite;
          s->Glyphs = ps->Glyphs;
          ps->Glyphs = RootlessGlyphs;
      }
  #endif
  
      // WRAP(ClearToBackground); fixme put this back? useful for shaped wins?
      // WRAP(RestoreAreas); fixme put this back?
  
  #undef WRAP
  }
  
  
  /*
   * RootlessInit
   *  Called by the rootless implementation to initialize the rootless layer.
   *  Rootless wraps lots of stuff and needs a bunch of devPrivates.
   */
  Bool RootlessInit(ScreenPtr pScreen, RootlessFrameProcsPtr procs)
  {
      RootlessScreenRec *s;
  
      if (!RootlessAllocatePrivates(pScreen))
          return FALSE;
  
      s = (RootlessScreenRec*)
          pScreen->devPrivates[rootlessScreenPrivateIndex].ptr;
  
      s->imp = procs;
  
      RootlessWrap(pScreen);
  
      if (!RegisterBlockAndWakeupHandlers(RootlessBlockHandler,
                                          RootlessWakeupHandler,
                                          (pointer) pScreen))
      {
          return FALSE;
      }
  
      return TRUE;
  }
  