diff options
author | Matthias Schiffer <mschiffer@universe-factory.net> | 2011-07-12 02:56:30 +0200 |
---|---|---|
committer | Matthias Schiffer <mschiffer@universe-factory.net> | 2011-07-12 02:56:30 +0200 |
commit | ade33320e5ea201a847bb9ee5522ee58b1cd8cb6 (patch) | |
tree | e688d8aa88bec3a667becd408d9e4733f3d8d1a7 | |
download | phi-ade33320e5ea201a847bb9ee5522ee58b1cd8cb6.tar phi-ade33320e5ea201a847bb9ee5522ee58b1cd8cb6.zip |
Implemented basic panel windows with pseudo-transparency
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | LICENSE | 24 | ||||
-rw-r--r-- | Setup.lhs | 3 | ||||
-rw-r--r-- | phi.cabal | 21 | ||||
-rw-r--r-- | src/Phi.hs | 6 | ||||
-rw-r--r-- | src/Phi/Panel.hs | 40 | ||||
-rw-r--r-- | src/Phi/X11.hs | 236 |
7 files changed, 332 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..733412c --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*~ +dist @@ -0,0 +1,24 @@ +Copyright (c) 2011, Matthias Schiffer <mschiffer@universe-factory.net> +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the <organization> nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Setup.lhs b/Setup.lhs new file mode 100644 index 0000000..5bde0de --- /dev/null +++ b/Setup.lhs @@ -0,0 +1,3 @@ +#!/usr/bin/env runhaskell +> import Distribution.Simple +> main = defaultMain diff --git a/phi.cabal b/phi.cabal new file mode 100644 index 0000000..e82b01e --- /dev/null +++ b/phi.cabal @@ -0,0 +1,21 @@ +cabal-version: >= 1.2 +name: phi +version: 0.1 +synopsis: An X panel +description: An X panel +category: X11 +license: BSD3 +license-file: LICENSE +author: Matthias Schiffer +maintainer: mschiffer@universe-factory.net +build-type: Simple + +library + build-depends: base >= 4, mtl, cairo, X11 + exposed-modules: Phi.Panel + hs-source-dirs: src + +executable Phi + build-depends: base >= 4 + hs-source-dirs: src + main-is: Phi.hs diff --git a/src/Phi.hs b/src/Phi.hs new file mode 100644 index 0000000..9aa6ad0 --- /dev/null +++ b/src/Phi.hs @@ -0,0 +1,6 @@ +import Phi.Panel +import Phi.X11 + +main :: IO () +main = do + initPhi phiDefaultXConfig { phiPanelPosition = PanelPositionBottom }
\ No newline at end of file diff --git a/src/Phi/Panel.hs b/src/Phi/Panel.hs new file mode 100644 index 0000000..23b022f --- /dev/null +++ b/src/Phi/Panel.hs @@ -0,0 +1,40 @@ +{-# LANGUAGE ExistentialQuantification #-} + +module Phi.Panel ( Panel(..) + , PanelClass(..) + , (<~>) + , separator + ) where + +import Data.Function + +class PanelClass a where + minSize :: a -> Int + + weight :: a -> Float + weight _ = 0 + +data Panel = forall a. PanelClass a => Panel a + +instance PanelClass Panel where + minSize (Panel p) = minSize p + weight (Panel p) = weight p + +data CompoundPanel = CompoundPanel Panel Panel + +instance PanelClass CompoundPanel where + minSize (CompoundPanel a b) = ((+) `on` minSize) a b + weight (CompoundPanel a b) = ((+) `on` weight) a b + +(<~>) :: Panel -> Panel -> Panel +a <~> b = Panel $ CompoundPanel a b + + +data Separator = Separator Int Float + +instance PanelClass Separator where + minSize (Separator s _) = s + weight (Separator _ w) = w + +separator :: Int -> Float -> Panel +separator s w = Panel $ Separator s w diff --git a/src/Phi/X11.hs b/src/Phi/X11.hs new file mode 100644 index 0000000..06bebfa --- /dev/null +++ b/src/Phi/X11.hs @@ -0,0 +1,236 @@ +{-# LANGUAGE GeneralizedNewtypeDeriving #-} + +module Phi.X11 ( PanelPosition(..) + , PhiXConfig(..) + , phiDefaultXConfig + , initPhi + ) where + +import Graphics.X11.Xlib +import Graphics.X11.Xlib.Extras +import Graphics.X11.Xinerama + +import Control.Monad +import Data.Maybe +import Data.Bits + +import Control.Monad.State +import Control.Monad.Trans + +data PanelPosition = PanelPositionTop | PanelPositionBottom + +data PhiXConfig = PhiXConfig { phiXScreenInfo :: Display -> IO [Rectangle] + , phiPanelPosition :: PanelPosition + , phiPanelSize :: Int + } + +data PhiState = PhiState { phiXConfig :: PhiXConfig + , phiDisplay :: Display + , phiRootPixmap :: Pixmap + , phiPanels :: [PanelState] + } + +data PanelState = PanelState { panelWindow :: Window + , panelGC :: GC + , panelArea :: Rectangle + , panelScreenArea :: Rectangle + } + +newtype Phi a = Phi (StateT PhiState IO a) + deriving (Monad, MonadState PhiState, MonadIO) + +runPhi :: PhiState -> Phi a -> IO (a, PhiState) +runPhi st (Phi a) = runStateT a st + + +phiDefaultXConfig = PhiXConfig { phiXScreenInfo = getScreenInfo + , phiPanelPosition = PanelPositionTop + , phiPanelSize = 24 + } + + +initPhi :: PhiXConfig -> IO () +initPhi config = do + disp <- openDisplay [] + selectInput disp (defaultRootWindow disp) $ propertyChangeMask.|.structureNotifyMask + + runPhi PhiState { phiXConfig = config, phiDisplay = disp, phiRootPixmap = 0, phiPanels = [] } $ do + updateRootPixmap + + screens <- liftIO $ phiXScreenInfo config disp + panels <- mapM createPanel screens + forM_ panels $ \panel -> do + setPanelProperties panel + liftIO $ mapWindow disp (panelWindow panel) + + modify $ \state -> state { phiPanels = panels } + + state <- get + liftIO $ allocaXEvent $ \xevent -> runPhi state $ do + forever $ do + liftIO $ nextEvent disp xevent + event <- liftIO $ getEvent xevent + + case event of + ExposeEvent {} -> updatePanels + PropertyEvent {} -> handlePropertyUpdate event + _ -> return () + return () + + +updatePanels :: Phi () +updatePanels = do + disp <- gets phiDisplay + rootPixmap <- gets phiRootPixmap + panels <- gets phiPanels + forM_ panels $ \panel -> liftIO $ withRectangle (panelArea panel) (copyArea disp rootPixmap (panelWindow panel) (panelGC panel)) 0 0 + + +handlePropertyUpdate :: Event -> Phi () +handlePropertyUpdate PropertyEvent { ev_atom = atom } = do + disp <- gets phiDisplay + panels <- gets phiPanels + atom_XROOTPMAP_ID <- liftIO $ internAtom disp "_XROOTPMAP_ID" False + atom_XROOTMAP_ID <- liftIO $ internAtom disp "_XROOTMAP_ID" False + + when (atom == atom_XROOTPMAP_ID || atom == atom_XROOTMAP_ID) $ do + updateRootPixmap + updatePanels + + +updateRootPixmap :: Phi () +updateRootPixmap = do + disp <- gets phiDisplay + let screen = defaultScreen disp + rootwin = defaultRootWindow disp + atom_XROOTPMAP_ID <- liftIO $ internAtom disp "_XROOTPMAP_ID" False + atom_XROOTMAP_ID <- liftIO $ internAtom disp "_XROOTMAP_ID" False + pixmap <- liftM (fromMaybe 0 . listToMaybe . join . catMaybes) $ forM [atom_XROOTPMAP_ID, atom_XROOTMAP_ID] $ \atom -> liftIO $ rawGetWindowProperty 32 disp atom rootwin + modify $ \state -> state { phiRootPixmap = pixmap } + + +createPanel :: Rectangle -> Phi PanelState +createPanel screen = do + config <- gets phiXConfig + let rect = panelBounds config screen + disp <- gets phiDisplay + win <- createPanelWindow rect + gc <- liftIO $ createGC disp win + return PanelState { panelWindow = win, panelGC = gc, panelArea = rect, panelScreenArea = screen } + + +createPanelWindow :: Rectangle -> Phi Window +createPanelWindow rect = do + disp <- gets phiDisplay + let screen = defaultScreen disp + depth = defaultDepth disp screen + visual = defaultVisual disp screen + colormap = defaultColormap disp screen + rootwin = defaultRootWindow disp + mask = cWEventMask.|.cWColormap.|.cWBackPixel.|.cWBorderPixel + + liftIO $ allocaSetWindowAttributes $ \attr -> do + set_colormap attr colormap + set_background_pixel attr 0 + set_border_pixel attr 0 + set_event_mask attr exposureMask + withRectangle rect (createWindow disp rootwin) 0 depth inputOutput visual mask attr + + +setPanelProperties :: PanelState -> Phi () +setPanelProperties panel = do + disp <- gets phiDisplay + liftIO $ do + atom_NET_WM_WINDOW_TYPE <- internAtom disp "_NET_WM_WINDOW_TYPE" False + atom_NET_WM_WINDOW_TYPE_DOCK <- internAtom disp "_NET_WM_WINDOW_TYPE_DOCK" False + + atom_NET_WM_DESKTOP <- internAtom disp "_NET_WM_DESKTOP" False + + atom_NET_WM_STATE <- internAtom disp "_NET_WM_STATE" False + atom_NET_WM_STATE_SKIP_PAGER <- internAtom disp "_NET_WM_STATE_SKIP_PAGER" False + atom_NET_WM_STATE_SKIP_TASKBAR <- internAtom disp "_NET_WM_STATE_SKIP_TASKBAR" False + atom_NET_WM_STATE_STICKY <- internAtom disp "_NET_WM_STATE_STICKY" False + atom_NET_WM_STATE_BELOW <- internAtom disp "_NET_WM_STATE_BELOW" False + + atom_MOTIF_WM_HINTS <- internAtom disp "_MOTIF_WM_HINTS" False + + storeName disp (panelWindow panel) "Phi" + changeProperty32 disp (panelWindow panel) atom_NET_WM_WINDOW_TYPE aTOM propModeReplace [fromIntegral atom_NET_WM_WINDOW_TYPE_DOCK] + changeProperty32 disp (panelWindow panel) atom_NET_WM_DESKTOP cARDINAL propModeReplace [0xFFFFFFFF] + changeProperty32 disp (panelWindow panel) atom_NET_WM_STATE aTOM propModeReplace [ fromIntegral atom_NET_WM_STATE_SKIP_PAGER + , fromIntegral atom_NET_WM_STATE_SKIP_TASKBAR + , fromIntegral atom_NET_WM_STATE_STICKY + , fromIntegral atom_NET_WM_STATE_BELOW] + setWMHints disp (panelWindow panel) WMHints { wmh_flags = fromIntegral inputHintBit + , wmh_input = False + , wmh_initial_state = 0 + , wmh_icon_pixmap = 0 + , wmh_icon_window = 0 + , wmh_icon_x = 0 + , wmh_icon_y = 0 + , wmh_icon_mask = 0 + , wmh_window_group = 0 + } + changeProperty32 disp (panelWindow panel) atom_MOTIF_WM_HINTS atom_MOTIF_WM_HINTS propModeReplace [ 2, 0, 0, 0, 0 ] + setStruts panel + + +setStruts :: PanelState -> Phi () +setStruts panel = do + disp <- gets phiDisplay + config <- gets phiXConfig + let rootwin = defaultRootWindow disp + position = phiPanelPosition config + area = panelArea panel + (_, _, _, _, rootHeight, _, _) <- liftIO $ getGeometry disp rootwin + + let struts = case position of + PanelPositionTop -> [ 0 + , 0 + , (fromIntegral $ rect_y area) + (fromIntegral $ rect_height area) + , 0 + , 0 + , 0 + , 0 + , 0 + , (fromIntegral $ rect_x area) + , (fromIntegral $ rect_x area) + (fromIntegral $ rect_width area) - 1 + , 0 + , 0 + ] + PanelPositionBottom -> [ 0 + , 0 + , 0 + , (fromIntegral rootHeight) - (fromIntegral $ rect_y area) + , 0 + , 0 + , 0 + , 0 + , 0 + , 0 + , (fromIntegral $ rect_x area) + , (fromIntegral $ rect_x area) + (fromIntegral $ rect_width area) - 1 + ] + + liftIO $ do + atom_NET_WM_STRUT <- internAtom disp "_NET_WM_STRUT" False + atom_NET_WM_STRUT_PARTIAL <- internAtom disp "_NET_WM_STRUT_PARTIAL" False + + changeProperty32 disp (panelWindow panel) atom_NET_WM_STRUT cARDINAL propModeReplace $ take 4 struts + changeProperty32 disp (panelWindow panel) atom_NET_WM_STRUT_PARTIAL cARDINAL propModeReplace struts + + +panelBounds :: PhiXConfig -> Rectangle -> Rectangle +panelBounds config screenBounds = case phiPanelPosition config of + PanelPositionTop -> screenBounds { rect_height = fromIntegral $ phiPanelSize config } + PanelPositionBottom -> screenBounds { rect_height = fromIntegral $ phiPanelSize config, + rect_y = (rect_y screenBounds) + (fromIntegral $ rect_height screenBounds) - (fromIntegral $ phiPanelSize config) } + +withRectangle :: Rectangle -> (Position -> Position -> Dimension -> Dimension -> a) -> a +withRectangle r = withDimension r . withPosition r + +withPosition :: Rectangle -> (Position -> Position -> a) -> a +withPosition r f = f (rect_x r) (rect_y r) + +withDimension :: Rectangle -> (Dimension -> Dimension -> a) -> a +withDimension r f = f (rect_width r) (rect_height r) |