GraphicsF

{-# LANGUAGE CPP #-}
module GraphicsF(GraphicsF,setCursorSolid,setGfxEventMask,
                 setAdjustSize,setCursor,setDoubleBuffer,
                 graphicsF,graphicsF',
		 graphicsGroupF,graphicsGroupF',
		 graphicsDispGroupF,graphicsDispGroupF',
		 graphicsLabelF,graphicsLabelF',
		 graphicsDispF,graphicsDispF',
		 GfxEventMask(..),GfxChange(..),GfxCommand(..),GfxEvent(..),
                 GfxFCmd,GfxFEvent,
		 replaceGfx,replaceAllGfx,showGfx,highlightGfx) where
import Fudget
import FudgetIO
import Xcommand
import FRequest
import NullF(putK,putsK,getK,nullF)
import Spops(nullSP)
import CompSP(postMapSP)
import SpEither(filterLeftSP)--mapFilterSP
--import SerCompF(stubF)
import Command
import DrawInPixmap(pmFillRectangle,pmDrawPoint)
--import DrawInWindow(wCopyArea)
import Event
import Xtypes
import Geometry
import Gc
import Pixmap
import Cursor
--import Color
--import Font(font_id,string_box_size)
--import LoadFont(safeLoadQueryFont)
import BgF(changeGetBackPixel)
import Defaults(fgColor,bgColor,paperColor,labelFont)
import CmdLineEnv(argFlag,argKeyList,argReadKey)
import LayoutRequest
import Alignment
import Spacers(noStretchS,compS,minSizeS)
import Message
import CompOps
import CompSP(idRightSP)
import Dlayout(groupF)
import Utils(number,pairwith)
import HbcUtils(mapFst,mapSnd)
--import InputMsg
import Graphic
import CompiledGraphics
import MeasuredGraphics(MeasuredGraphics(SpacedM,MarkM),compileMG,DPath(..))--,emptyMG,emptyMG'
import Graphic2Pixmap
import GCtx(GCtx(..),wCreateGCtx,rootGCtx)
import GCAttrs
import MGOps(parentGctx,replaceMGPart,updateMGPart,groupMGParts,ungroupMGParts)
import IdempotSP
import DrawCompiledGraphics
import Rects(intersectRects,overlaps)
import EitherUtils(stripEither)--,mapEither
import Sizing(newSize,Sizing(..))
--import HO(apSnd)
--import Maybe(fromMaybe)
import Xrequest(xrequestK)
import StdIoUtil(echoStderrK)
--import ContinuationIO(stderr)
--import Maptrace(ctrace) -- debugging

import FDefaults
{-
HBC uses "cpp -C -traditional" which causes all the  to be left behind
when the macro definitions are processed. That is why the definitions
are inside a comment.







  
-}


-- Syntax for existential quantification varies:


--  Commands for grapihcsF: ---------------------------------------------------

data GfxChange gfx
  = GfxReplace (Bool,Maybe gfx)
  | GfxGroup Int Int -- position & length
  | GfxUngroup Int -- position
  
data GfxCommand path gfx
  = ChangeGfx [(path,GfxChange gfx)]
  | ChangeGfxBg ColorSpec
  | ChangeGfxBgPixmap PixmapId Bool -- True = free pixmap
  | forall bg . (Graphic bg) => ChangeGfxBgGfx bg
  | ChangeGfxCursor CursorId
  | ChangeGfxFontCursor Int
  | ShowGfx path (Maybe Alignment,Maybe Alignment) -- makes the selected part visible
  | BellGfx Int -- sound the bell
  | GetGfxPlaces [path] -- ask for rectangles of listed paths

replaceAllGfx = replaceGfx []
replaceGfx path gfx = ChangeGfx [(path,GfxReplace (False,Just gfx))]
showGfx path = ShowGfx path (Nothing,Nothing)
highlightGfx path on = ChangeGfx [(path,GfxReplace (on,Nothing))]

instance Functor GfxChange where
  fmap f (GfxReplace r) = GfxReplace (fmap (fmap f) r)
  fmap f (GfxGroup from count) = GfxGroup from count
  fmap f (GfxUngroup at) = GfxUngroup at

instance Functor (GfxCommand path) where
  fmap f cmd =
    case cmd of
      ChangeGfx changes -> ChangeGfx (mapSnd (fmap f) changes)
      -- _ -> cmd -- Operationally, the rest is the same as this line.
      ChangeGfxBg c -> ChangeGfxBg c
      ChangeGfxBgPixmap pm b -> ChangeGfxBgPixmap pm b
      ChangeGfxBgGfx gfx -> ChangeGfxBgGfx gfx
      ChangeGfxCursor cursor -> ChangeGfxCursor cursor
      ChangeGfxFontCursor shape -> ChangeGfxFontCursor shape
      ShowGfx path a -> ShowGfx path a
      BellGfx n -> BellGfx n
      GetGfxPlaces paths -> GetGfxPlaces paths

--  Events from graphicsF: ----------------------------------------------------

data GfxEvent path
  = GfxButtonEvent { gfxTime  :: Time,
                     gfxState :: ModState,
		     gfxType  :: Pressed,
                     gfxButton:: Button,
		     gfxPaths :: [(path,(Point,Rect))] }
  | GfxMotionEvent { gfxTime  :: Time,
                     gfxState :: ModState,
		     gfxPaths :: [(path,(Point,Rect))] }
  | GfxKeyEvent    { gfxTime  :: Time,
                     gfxState::ModState,
                     gfxKeySym::KeySym,
		     gfxKeyLookup::KeyLookup }
  | GfxFocusEvent  { gfxHasFocus :: Bool }
  | GfxPlaces [Rect] -- response to GetGfxPlaces
  | GfxResized Size
  deriving (Eq,Show)


--  graphicsF event masks: ----------------------------------------------------

data GfxEventMask = GfxButtonMask | GfxMotionMask | GfxDragMask | GfxKeyMask

allGfxEvents = [GfxButtonMask, GfxMotionMask, GfxDragMask, GfxKeyMask]
gfxMouseMask = [GfxButtonMask, GfxDragMask] -- backward compat

gfxEventMask = concatMap events
  where
    events GfxButtonMask = buttonmask
    events GfxMotionMask = motionmask
    events GfxDragMask   = dragmask
    events GfxKeyMask    = keventmask

    buttonmask = [ButtonPressMask,ButtonReleaseMask]
    motionmask = [PointerMotionMask]
    dragmask = [Button1MotionMask]
    keventmask =
	 [KeyPressMask,
          EnterWindowMask, LeaveWindowMask -- because of CTT implementation
	 ]

--  Customisers for graphicsF: ------------------------------------------------

newtype GraphicsF gfx = Pars [Pars gfx]

data Pars gfx
  -- Standard customisers:
  = BorderWidth Int
  | BgColorSpec ColorSpec
  | FgColorSpec ColorSpec
  | FontSpec FontSpec
  | Sizing Sizing
  | Stretchable (Bool,Bool)
  | InitSize gfx
  | InitDisp gfx
  -- Special customisers:
  | CursorSolid Bool
  | GfxEventMask [GfxEventMask]
  | AdjustSize Bool
  | Cursor Int -- pointer cursor shape for XCreateFontCursor
  | DoubleBuffer Bool
-- Could also support:
--  | Align Alignment
--  | Spacer Spacer
--  | Margin Int

instance HasBorderWidth (GraphicsF a) where {  setBorderWidth p (Pars ps) = Pars (BorderWidth p:ps);   getBorderWidthMaybe (Pars ps) = getparMaybe (\x->case x of BorderWidth p -> Just p; _-> Nothing) ps }
instance HasBgColorSpec (GraphicsF a) where {  setBgColorSpec p (Pars ps) = Pars (BgColorSpec p:ps);   getBgColorSpecMaybe (Pars ps) = getparMaybe (\x->case x of BgColorSpec p -> Just p; _-> Nothing) ps }
instance HasFgColorSpec (GraphicsF a) where {  setFgColorSpec p (Pars ps) = Pars (FgColorSpec p:ps);   getFgColorSpecMaybe (Pars ps) = getparMaybe (\x->case x of FgColorSpec p -> Just p; _-> Nothing) ps }
instance HasSizing (GraphicsF a) where {  setSizing p (Pars ps) = Pars (Sizing p:ps);   getSizingMaybe (Pars ps) = getparMaybe (\x->case x of Sizing p -> Just p; _-> Nothing) ps }
instance HasFontSpec (GraphicsF a) where {  setFontSpec p (Pars ps) = Pars (FontSpec p:ps);   getFontSpecMaybe (Pars ps) = getparMaybe (\x->case x of FontSpec p -> Just p; _-> Nothing) ps }
instance HasStretchable (GraphicsF a) where {  setStretchable p (Pars ps) = Pars (Stretchable p:ps);   getStretchableMaybe (Pars ps) = getparMaybe (\x->case x of Stretchable p -> Just p; _-> Nothing) ps }
instance HasInitSize (GraphicsF) where {  setInitSize p (Pars ps) = Pars (InitSize p:ps);   getInitSizeMaybe (Pars ps) = getparMaybe (\x->case x of InitSize p -> Just p; _-> Nothing) ps }
instance HasInitDisp (GraphicsF) where {  setInitDisp p (Pars ps) = Pars (InitDisp p:ps);   getInitDispMaybe (Pars ps) = getparMaybe (\x->case x of InitDisp p -> Just p; _-> Nothing) ps }
setCursorSolid p = cust (\ (Pars ps) -> Pars (CursorSolid p:ps)); getCursorSolid (Pars ps) = getpar (\x->case x of CursorSolid p -> Just p; _-> Nothing) ps; getCursorSolidMaybe (Pars ps) = getparMaybe (\x->case x of CursorSolid p -> Just p; _-> Nothing) ps
setGfxEventMask p = cust (\ (Pars ps) -> Pars (GfxEventMask p:ps)); getGfxEventMask (Pars ps) = getpar (\x->case x of GfxEventMask p -> Just p; _-> Nothing) ps; getGfxEventMaskMaybe (Pars ps) = getparMaybe (\x->case x of GfxEventMask p -> Just p; _-> Nothing) ps
setAdjustSize p = cust (\ (Pars ps) -> Pars (AdjustSize p:ps)); getAdjustSize (Pars ps) = getpar (\x->case x of AdjustSize p -> Just p; _-> Nothing) ps; getAdjustSizeMaybe (Pars ps) = getparMaybe (\x->case x of AdjustSize p -> Just p; _-> Nothing) ps
setCursor p = cust (\ (Pars ps) -> Pars (Cursor p:ps)); getCursor (Pars ps) = getpar (\x->case x of Cursor p -> Just p; _-> Nothing) ps; getCursorMaybe (Pars ps) = getparMaybe (\x->case x of Cursor p -> Just p; _-> Nothing) ps
setDoubleBuffer p = cust (\ (Pars ps) -> Pars (DoubleBuffer p:ps)); getDoubleBuffer (Pars ps) = getpar (\x->case x of DoubleBuffer p -> Just p; _-> Nothing) ps; getDoubleBufferMaybe (Pars ps) = getparMaybe (\x->case x of DoubleBuffer p -> Just p; _-> Nothing) ps

-------------------------------------------------------------------------------

type GfxFCmd a = GfxCommand DPath a
type GfxFEvent = GfxEvent DPath

graphicsDispF :: Graphic a => F (GfxFCmd a) (GfxFEvent)
graphicsDispF = graphicsDispF' standard

graphicsLabelF lbl = graphicsLabelF' standard lbl

graphicsLabelF' customiser gfx = nullSP >^^=< d >=^^< nullSP'
  where d = graphicsF' (customiser . params)
	params = setInitDisp gfx .setGfxEventMask [] . setSizing Static .
		 setBgColor bgColor . setBorderWidth 0
	nullSP' = nullSP -- :: (SP anything (GfxCommand MeasuredGraphics))
	-- this is a workaround necessary to resolve the overloading

graphicsDispF' :: Graphic gfx => Customiser (GraphicsF gfx) -> F (GfxFCmd gfx) (GfxFEvent)
graphicsDispF' customiser  = graphicsF' (customiser . dispCustomiser)
graphicsDispGroupF fud = graphicsGroupF' dispCustomiser fud
graphicsDispGroupF' customiser fud =
  graphicsGroupF' (customiser . dispCustomiser) fud

dispCustomiser =
  setCursorSolid True . setGfxEventMask gfxMouseMask . setSizing Growing

graphicsF :: Graphic gfx => F (GfxFCmd gfx) (GfxFEvent)
graphicsF = graphicsF' standard

graphicsF' custom = filterLeftSP >^^=< graphicsGroupF' custom nullF >=^< Left

graphicsGroupF :: Graphic gfx => F i o -> F (Either (GfxFCmd gfx) i) (Either (GfxFEvent) o)
graphicsGroupF = graphicsGroupF' standard

--graphicsGroupF' :: (Graphic init,Graphic gfx => Customiser (GraphicsF init) -> F i o -> F (Either (GfxFCmd gfx) i) (Either (GfxFEvent) o)
graphicsGroupF' :: Graphic gfx => Customiser (GraphicsF gfx) -> F i o -> F (Either (GfxFCmd gfx) i) (Either (GfxFEvent) o)
graphicsGroupF' customiser fud = 
  let solid = getCursorSolid params
      mask = getGfxEventMask params
      sizing = getSizing params
      adjsize = getAdjustSize params
      doublebuffer = getDoubleBuffer params
      optcursor = getCursorMaybe params
      font = getFontSpec params
      bw = getBorderWidth params
      bgcol = getBgColorSpec params
      fgcol = getFgColorSpec params
      optx = getInitDispMaybe params
      optstretch = getStretchableMaybe params
      optinitsize = getInitSizeMaybe params
      params = customiser defaults
      defaults = Pars [BorderWidth 1,
                       BgColorSpec (colorSpec paperColor),
                       FgColorSpec (colorSpec fgColor),
		       Sizing Dynamic,
		       CursorSolid False,
		       GfxEventMask allGfxEvents,
		       AdjustSize True,
		       DoubleBuffer defaultdoublebuffer,
		       FontSpec (fontSpec labelFont)]
      eventmask = ExposureMask:
	          gfxEventMask mask
      --grabmask =  [ButtonReleaseMask, PointerMotionMask]
      -- NOTE: some code below assumes that motion events occur ONLY
      --       while Button1 is pressed!
      startcmds = [ChangeWindowAttributes [CWEventMask eventmask,
                                           CWBitGravity NorthWestGravity],
                    ConfigureWindow [CWBorderWidth bw]--,
		    --GrabButton False (Button 1) [] grabmask,
		    --GrabButton False (Button 2) [] [ButtonReleaseMask]
		  ]
  in --compMsgSP layoutOptSP (idRightSP idempotSP) `serCompSP`
     idRightSP (stripEither `postMapSP` idRightSP idempotSP) >^^=<
     groupF (fmap XCmd startcmds)
        (initK doublebuffer font optcursor fgcol bgcol $
	 graphicsK0 solid sizing adjsize optstretch optinitsize optx)
        fud

dbeSwapBuffers cont =
  xrequestK (DbeSwapBuffers swapaction) Just $ \ (DbeBuffersSwapped _) -> cont

optDoubleBufferK False cont = cont Nothing
optDoubleBufferK True cont =
  xrequestK DbeQueryExtension Just $ \ (DbeExtensionQueried status major minor) ->
  let ok=status/=0
  in if not ok
     then echoStderrK "Sorry, double buffering not available." $
	  cont Nothing
     else xrequestK (DbeAllocateBackBufferName swapaction) Just $ \ (DbeBackBufferNameAllocated backbuf) ->
          --xcommandK ClearWindow $
          cont (Just backbuf)

initK doublebuffer font optcursor fgcol bgcol k =
  changeGetBackPixel bgcol $ \ bg ->
  maybe id setFontCursor optcursor $
  convColorK [fgcol,colorSpec "black"] $ \ fg ->
  wCreateGCtx rootGCtx [GCFont [font,fontSpec "fixed"],GCForeground fg,GCBackground bg] $ \ gctx@(GC gc _) ->
  wCreateGC rootGC [GCForeground bg] $ \ cleargc ->
  createCursorGC gc bg fg $ \ higc ->
  optDoubleBufferK doublebuffer $ \ optbackbuf ->
  k optbackbuf gctx bg cleargc higc

optCompileGraphicK gctx optgfx cont =
  case optgfx of
    Nothing -> cont Nothing
    Just gfx ->
      measureGraphicK gfx gctx $ \ mg ->
      cont (Just (mg,compileMG id mg))

graphicsK0 solid sizing adjsize optstretch optinitsize optx optbackbuf gctx bg cleargc higc =
    graphicsK1 
  where
    graphicsK1 =
      optCompileGraphicK gctx optinitsize $ \ optcgsize ->
      optCompileGraphicK gctx optx $ \ optcgx ->
      graphicsK2 optcgsize optcgx
    graphicsK2 optcgsize optcgx =
        graphicsK init
      where
	optSizeS    = fmap (minSizeS . minsize . snd . snd) optcgsize
        optStretchS = fmap stretchS optstretch
          where stretchS (sh,sv) = noStretchS (not sh) (not sv)
        spacerM =
	  case (optStretchS,optSizeS) of
	    (Just stretchS,Just sizeS) -> SpacedM (stretchS `compS` sizeS)
	    (Just stretchS,_         ) -> SpacedM stretchS
	    (_            ,Just sizeS) -> SpacedM sizeS
	    _                          -> MarkM gctx

        -- All incoming and outgoing paths have to be adjusted because of
	-- the extra spacer. The functions pathIn & pathOut handle this.
	init = pairwith (compileMG id) $ spacerM $ maybe (emptyMG 10) fst optcgx
         -- Stretchiness is applied to all drawings as it should be, but
	 -- optinitsize should be applied only to the first drawing!!!
    
    pathIn path = 0:path
    -- pathOut (0:path) = path

    -- locatePointOut p = mapFst pathOut . locatePoint p
    locatePointOut p (CGMark cg) = locatePoint p cg
    -- bug if top node isn't a CGMark !!

    graphicsK (mg,(cg,req)) =
      putLayoutReq req $
      idleK cleargc req mg cg solid []

    idleK cleargc req mg cg active es =
        seq size $ -- prevents a space leak when sizing==Dynamic, TH 980724
	getK $ message lowK highK
      where
	size = minsize req -- == current window size most of the time
	curR = hiR (solid||active)
	same = idleK cleargc req mg cg active es
	newcleargc cleargc' = idleK cleargc' req mg cg active es

	optInsertNew mg cg gctx path optreq optnew k =
	  case optnew of
	    Nothing  -> k mg cg optreq
	    Just new -> measureGraphicK new gctx $ \ newmg ->
			let mg' = replaceMGPart mg path newmg
			    (cg',req) = compileMG (newSize sizing size) mg'
			in k mg' cg' (Just req)

	updGraphicsK mg cg optreq [] c =
	  case optreq of
	    Just req' | not (similar req' req)
		 	     -> --ctrace "updgfx" (show (req,req')) $
				putLayoutReq req' $ c req' mg cg False
	    _                -> c req mg cg True
	updGraphicsK mg cg optreq ((path,change):changes) c =
          case change of
            GfxReplace r -> replace r
            GfxGroup from count -> group from count
            GfxUngroup pos -> ungroup pos
          where
            replace (hi,optnew) =
              optInsertNew mg cg (parentGctx gctx mg path) path optreq optnew $ \ mg' cg' optreq' ->
              let cg'' = case (hi,optnew) of
                           (False,Nothing) -> cgupdate cg' path removecursor
                           (True,_)        -> cgupdate cg' path addcursor
                           _ -> cg'
              in updGraphicsK mg' cg'' optreq' changes c

            group from count = updGraphicsK mg' cg' optreq changes c
              where mg' = updateMGPart mg path (groupMGParts from count)
                    cg' = cgupdate cg path (cgGroup from count)

            ungroup pos = updGraphicsK mg' cg' optreq changes c
              where mg' = updateMGPart mg path (ungroupMGParts pos)
                    cg' = cgupdate cg path (cgUngroup pos)

        bufDrawChangesK = maybe drawChangesK backBufDrawChangesK optbackbuf
	bufDrawK = maybe drawK backBufDrawK optbackbuf

        backBufDrawChangesK backbuf beQuick cur new old changes cont =
            drawChangesK' (Just (DbeBackBuffer backbuf,cleargc)) False cur new old changes $
	    dbeSwapBuffers $
	    cont
	backBufDrawK backbuf cur clip cg cont =
            drawK' (DbeBackBuffer backbuf) cur clip cg $
	    dbeSwapBuffers $
	    --putLow (wCopyArea gc (DbeBackBuffer backbuf) (Rect 0 size) 0) $
	    cont
          where (GC gc _) = gctx

	buttonEvent t p state type' button =
	  -- High level output tagged Left is sent through idempotSP
	  putHigh (Left $
	           GfxButtonEvent t state type' button (locatePointOut p cg)) $
	  same
	motionEvent t p state =
	  -- High level output tagged Left is sent through idempotSP
	  putHigh (Left $ GfxMotionEvent t state (locatePointOut p cg)) $
	  same
	key t mods sym lookup =
	  putHigh (Right $ GfxKeyEvent t mods sym lookup) $ same

	highK (ShowGfx path align) = mkPathVisible cg (pathIn path) align same
	highK (BellGfx n) = xcommandK (Bell n) same

	highK (GetGfxPlaces paths) =
	  putHigh (Right $ GfxPlaces $ fmap (cgrect . cgpart cg . pathIn) paths) $
	  same
	highK (ChangeGfxBg bgspec) =
	  convColorK bgspec $ \ bgcol ->
	  xcommandK (ChangeWindowAttributes [CWBackPixel bgcol]) $
	  xcommandK clearWindowExpose $
	  wCreateGC rootGC [GCForeground bgcol] $ \ cleargc' ->
	  -- FreeGC cleargc
	  newcleargc cleargc'
	highK (ChangeGfxBgPixmap pixmap freeIt) =
	  xcommandK (ChangeWindowAttributes [CWBackPixmap pixmap]) $
	  xcommandK clearWindowExpose $
	  wCreateGC rootGC [GCFillStyle FillTiled,GCTile pixmap] $ \ cleargc' ->
	  -- FreeGC cleargc
	  (if freeIt then xcommandK (FreePixmap pixmap) else id) $
	  newcleargc cleargc'
        highK (ChangeGfxBgGfx gfx) =
	  graphic2PixmapImage gfx gctx $ \ (PixmapImage size pm) ->
	  highK (ChangeGfxBgPixmap pm True)
        highK (ChangeGfxCursor cursor) =
          defineCursor cursor $
          xcommandK Flush $
	  same
        highK (ChangeGfxFontCursor shape) =
          setFontCursor shape $
          xcommandK Flush $
	  same
	highK (ChangeGfx changes0) =
	    updGraphicsK mg cg Nothing changes $ \ req' mg' cg' beQuick ->
	    bufDrawChangesK beQuick (higc,curR) cg' cg (fmap fst changes) $
	    --mkChangeVisible cg' changes $
	    idleK cleargc req' mg' cg' active []
	  where changes = mapFst pathIn changes0

	changeActive active' =
	  if active'==active
	  then same
	  else putHigh (Left $ GfxFocusEvent { gfxHasFocus=active' }) $
	       bufDrawChangesK True (higc,hiR (solid||active')) cg cg (cursorPaths cg) $
	       idleK cleargc req mg cg active' es

        lowK (XEvt e) = eventK e
	lowK (LEvt lresp) = layoutK lresp
	lowK _ = same

        layoutK lresp =
	  case lresp of
	    LayoutSize size'
		| adjsize ->
		    if size' == size then same
		    else let cg'' = foldr restorecursor cg' (cgcursors cg)
			       where
			         restorecursor path cg = cgupdate cg path addcursor
				 (cg',_) = compileMG (const size') mg
			 in putHigh (Left $ GfxResized size') $
			    bufDrawChangesK True (higc,curR) cg'' cg [] $
			    idleK cleargc req' mg cg'' active es
		| otherwise -> idleK cleargc req' mg cg active es
	      where req' = mapLayoutSize (const size') req
	    _ -> same

	eventK event =
	  case event of
	    Expose r 0 ->
	      let rs = r:es
	      in bufDrawK (higc,curR) (intersectRects rs) (prune rs cg) $
                 idleK cleargc req mg cg active []
	    Expose r _ -> idleK cleargc req mg cg active (r:es)
	    FocusIn  {} -> changeActive True
	    FocusOut {} -> changeActive False

	    ButtonEvent {time=t, pos=pos, type'=type', button=button, state=state} ->
	       buttonEvent t pos state type' button
	    MotionNotify {time=t,pos=pos,state=state} -> motionEvent t pos state
	    KeyEvent t _ _ mods Pressed _ sym lookup -> key t mods sym lookup
	    _ -> same

prune rs (CGMark cg) = CGMark (prune rs cg)
prune rs (CGraphics r cur cmds cgs) =
  if any (overlaps r) rs
  then if null cmds --  || all (null.snd) cmds
       then CGraphics r cur cmds (fmap (prune rs) cgs)
       else CGraphics r cur cmds cgs
             -- cmds may overlap with cgs, so
	     -- if cmds are redrawn then all cgs should be redrawn too.
  else CGraphics r cur [] [] -- subtree rectangles are inside parent rectangles.

{-
locatePoint' p cg = fmap addrect $ locatePoint p cg
  where
    addrect = pairwith (cgrect . cgpart cg)
-}

locatePoint p (CGMark cg) = [(0:path,geom)|(path,geom)<-locatePoint p cg]
 --  ^^ the wrong geometry will be return if CGMark came from a SpacerM !!
locatePoint p (CGraphics r _ _ gs) =
  if p `inRect` r
  then let ps = fmap (locatePoint p) gs
       in case [ (i:path,pr) | (i,paths)<-number 1 ps, (path,pr)<-paths] of
            [] -> [([],(p-rectpos r,r))]
	    ps -> ps
  else []

cursorPaths (CGMark cg) = fmap (0:) (cursorPaths cg)
cursorPaths (CGraphics _ cur _ gs) =
  if cur
  then [[]]
  else [i:p | (i,g)<-number 1 gs, p<-cursorPaths g]

hiR True = solidCursorRects
hiR False = hollowCursorRects

solidCursorRects r = [r]

hollowCursorRects (Rect (Point x y) (Point w h)) =
   [rR x y w lw,rR x y lw h,rR x (y+h-lw) w lw,rR (x+w-lw) y lw h]
 where lw=minimum [2,w,h]

--putSize cg = putLayoutReq (Layout (cgsize cg) False False)

mkChangeVisible cg changes =
  case [ path | (path,(True,_))<-changes] of
    path:_ -> mkPathVisible cg path (Nothing,Nothing)
    _ -> id

mkPathVisible cg path align =
    putLayout (lMkVis (cgrect (cgpart cg path)))
  where
    lMkVis r = LayoutMakeVisible (r `growrect` 5) align
			 -- growrect compensates for a layout bug !!

putLayoutReq = putLayout . LayoutRequest
--putSpacer = putLayout . LayoutSpacer
putLayout = putK . Low . LCmd

createCursorGC gc bg fg cont =
  --allocNamedColorDefPixel defaultColormap cursorcolor "white" $ \ hipix ->
  tryConvColorK cursorcolor $ \ opthipix ->
  let hipix = fromMaybe fg opthipix
  in if hipix/=bg && hipix/=fg && not mono
     then wCreateGC gc [GCForeground hipix] $ \ cursorgc ->
	  cont cursorgc
     else createPixmap (Point 2 2) copyFromParent $ \ pm ->
	  wCreateGC gc [GCForeground bg] $ \ cleargc ->
	  putsK [Low $ pmFillRectangle pm cleargc (rR 0 0 2 2),
		 Low $ pmDrawPoint pm gc 0] $
	  wCreateGC gc [GCFillStyle FillTiled,GCTile pm] $ \ cursorgc ->
	  cont cursorgc

similar l1 l2 =
  minsize l1==minsize l2 &&
  fixedh l1==fixedh l2 &&
  fixedv l1==fixedv l2

cursorcolor = argKeyList "cursor" ["yellow"]
mono = argFlag "mono" False
defaultdoublebuffer = argFlag "doublebuffer" False
swapaction = argReadKey "swapaction" DbeCopied