packages feed

X11-extras-0.0: Graphics/X11/Xinerama.hsc

-- | Interface to Xinerama API
module Graphics.X11.Xinerama
  (XineramaScreenInfo(..), 
   xineramaIsActive, 
   xineramaQueryExtension, 
   xineramaQueryVersion, 
   xineramaQueryScreens,
   getScreenInfo) where

#include <config.h>

import Foreign
import Foreign.C.Types
import Graphics.X11.Xlib
import Control.Monad

-- | Representation of the XineramaScreenInfo struct
data XineramaScreenInfo = XineramaScreenInfo 
                          { xsi_screen_number :: CInt,
                            xsi_x_org         :: CShort,
                            xsi_y_org         :: CShort,
                            xsi_width         :: CShort,
                            xsi_height        :: CShort }
                            deriving (Show)

-- | Wrapper around xineramaQueryScreens that fakes a single screen when
-- Xinerama is not active. This is the preferred interface to
-- Graphics.X11.Xinerama.
getScreenInfo :: Display -> IO [Rectangle]
getScreenInfo dpy = maybe [singleScreen] (map xsiToRect) `liftM` xineramaQueryScreens dpy
    where dflt = defaultScreen dpy
          xsiToRect xsi = Rectangle
                        { rect_x        = fromIntegral $ xsi_x_org xsi
                        , rect_y        = fromIntegral $ xsi_y_org xsi
                        , rect_width    = fromIntegral $ xsi_width xsi
                        , rect_height   = fromIntegral $ xsi_height xsi
                        }
          singleScreen = Rectangle
                       { rect_x      = 0 
                       , rect_y      = 0 
                       , rect_width  = fromIntegral $ displayWidth dpy dflt
                       , rect_height = fromIntegral $ displayHeight dpy dflt
                       }

#ifdef HAVE_X11_EXTENSIONS_XINERAMA_H
-- We have Xinerama, so the library will actually work

-- for XFree() (already included from Xinerama.h, but I don't know if I can count on that.)
#include <X11/Xlib.h>

#include <X11/extensions/Xinerama.h>

instance Storable XineramaScreenInfo where
  sizeOf _ = #{size XineramaScreenInfo}
  -- FIXME: Is this right?
  alignment _ = alignment (undefined :: CInt)

  poke p xsi = do
    #{poke XineramaScreenInfo, screen_number } p $ xsi_screen_number xsi
    #{poke XineramaScreenInfo, x_org         } p $ xsi_x_org xsi
    #{poke XineramaScreenInfo, y_org         } p $ xsi_y_org xsi
    #{poke XineramaScreenInfo, width         } p $ xsi_width xsi
    #{poke XineramaScreenInfo, height        } p $ xsi_height xsi

  peek p = return XineramaScreenInfo
              `ap` (#{peek XineramaScreenInfo, screen_number} p)
              `ap` (#{peek XineramaScreenInfo, x_org} p)
              `ap` (#{peek XineramaScreenInfo, y_org} p)
              `ap` (#{peek XineramaScreenInfo, width} p)
              `ap` (#{peek XineramaScreenInfo, height} p)

foreign import ccall "XineramaIsActive"
  xineramaIsActive :: Display -> IO Bool

xineramaQueryExtension :: Display -> IO (Maybe (CInt, CInt))
xineramaQueryExtension dpy = wrapPtr2 (cXineramaQueryExtension dpy) go
  where go False _ _                = Nothing
        go True eventbase errorbase = Just (fromIntegral eventbase, fromIntegral errorbase)

xineramaQueryVersion :: Display -> IO (Maybe (CInt, CInt))
xineramaQueryVersion dpy = wrapPtr2 (cXineramaQueryVersion dpy) go
  where go False _ _        = Nothing
        go True major minor = Just (fromIntegral major, fromIntegral minor)

xineramaQueryScreens :: Display -> IO (Maybe [XineramaScreenInfo])
xineramaQueryScreens dpy = 
  withPool $ \pool -> do intp <- pooledMalloc pool
                         p <- cXineramaQueryScreens dpy intp
                         if p == nullPtr 
                            then return Nothing
                            else do nscreens <- peek intp
                                    screens <- peekArray (fromIntegral nscreens) p
                                    cXFree p
                                    return (Just screens)

foreign import ccall "XineramaQueryExtension"
  cXineramaQueryExtension :: Display -> Ptr CInt -> Ptr CInt -> IO Bool
foreign import ccall "XineramaQueryVersion"
  cXineramaQueryVersion :: Display -> Ptr CInt -> Ptr CInt -> IO Bool
foreign import ccall "XineramaQueryScreens"
  cXineramaQueryScreens :: Display -> Ptr CInt -> IO (Ptr XineramaScreenInfo)
foreign import ccall "XFree"
  cXFree :: Ptr a -> IO CInt

wrapPtr2 :: (Storable a, Storable b) => (Ptr a -> Ptr b -> IO c) -> (c -> a -> b -> d) -> IO d
wrapPtr2 cfun f = 
  withPool $ \pool -> do aptr <- pooledMalloc pool
                         bptr <- pooledMalloc pool
                         ret <- cfun aptr bptr
                         a <- peek aptr
                         b <- peek bptr
                         return (f ret a b)

#else

-- No Xinerama, but if we fake a non-active Xinerama interface, "getScreenInfo"
-- will continue to work fine in the single-screen case.
xineramaIsActive :: Display -> IO Bool
xineramaIsActive _ = return False

xineramaQueryExtension :: Display -> IO (Maybe (CInt, CInt))
xineramaQueryExtension _  = return Nothing

xineramaQueryVersion :: Display -> IO (Maybe (CInt, CInt))
xineramaQueryVersion _ = return Nothing

xineramaQueryScreens :: Display -> IO (Maybe [XineramaScreenInfo])
xineramaQueryScreens _ = return Nothing
#endif