aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--XMonad/Layout/OnHost.hs155
-rw-r--r--xmonad-contrib.cabal1
2 files changed, 156 insertions, 0 deletions
diff --git a/XMonad/Layout/OnHost.hs b/XMonad/Layout/OnHost.hs
new file mode 100644
index 0000000..e14a24c
--- /dev/null
+++ b/XMonad/Layout/OnHost.hs
@@ -0,0 +1,155 @@
+{-# LANGUAGE FlexibleInstances, MultiParamTypeClasses #-}
+
+-----------------------------------------------------------------------------
+-- |
+-- Module : XMonad.Layout.OnHost
+-- Copyright : (c) Brandon S Allbery, Brent Yorgey
+-- License : BSD-style (see LICENSE)
+--
+-- Maintainer : <allbery.b@gmail.com>
+-- Stability : unstable
+-- Portability : unportable
+--
+-- Configure layouts on a per-host basis: use layouts and apply
+-- layout modifiers selectively, depending on the host. Heavily based on
+-- XMonad.Layout.PerWorkspace by Brent Yorgey.
+-----------------------------------------------------------------------------
+
+module XMonad.Layout.OnHost (-- * Usage
+ -- $usage
+ OnHost
+ ,onHost
+ ,onHosts
+ ,modHost
+ ,modHosts
+ ) where
+
+import XMonad
+import qualified XMonad.StackSet as W
+
+import XMonad.Layout.LayoutModifier
+
+import Data.Maybe (fromMaybe)
+import System.Posix.Env (getEnv)
+
+-- $usage
+-- You can use this module by importing it into your ~\/.xmonad\/xmonad.hs file:
+--
+-- > import XMonad.Layout.OnHost
+--
+-- and modifying your layoutHook as follows (for example):
+--
+-- > layoutHook = modHost "baz" m1 $ -- apply layout modifier m1 to all layouts on host "baz"
+-- > onHost "foo" l1 $ -- layout l1 will be used on host "foo".
+-- > onHosts ["bar","quux"] l2 $ -- layout l2 will be used on hosts "bar" and "quux".
+-- > l3 -- layout l3 will be used on all other hosts.
+--
+-- Note that @l1@, @l2@, and @l3@ can be arbitrarily complicated
+-- layouts, e.g. @(Full ||| smartBorders $ tabbed shrinkText
+-- defaultTConf ||| ...)@, and @m1@ can be any layout modifier, i.e. a
+-- function of type @(l a -> ModifiedLayout lm l a)@.
+--
+-- In another scenario, suppose you wanted to have layouts A, B, and C
+-- available on all hosts, except that on host foo you want
+-- layout D instead of C. You could do that as follows:
+--
+-- > layoutHook = A ||| B ||| onHost "foo" D C
+--
+-- Note that we rely on '$HOST' being set in the environment, as is true on most
+-- modern systems; if it's not, you may want to use a wrapper around xmonad or
+-- perhaps use 'System.Posix.Env.setEnv' (or 'putEnv') to set it in 'main'.
+-- This is to avoid dragging in the network package as an xmonad dependency.
+-- If '$HOST' is not defined, it will behave as if the host name never matches.
+--
+-- Also note that '$HOST' is usually a fully qualified domain name, not a short name.
+-- If you use a short name, this code will try to truncate $HOST to match; this may
+-- prove too magical, though, and may change in the future.
+
+-- | Specify one layout to use on a particular host, and another
+-- to use on all others. The second layout can be another call to
+-- 'onHost', and so on.
+onHost :: (LayoutClass l1 a, LayoutClass l2 a)
+ => String -- ^ the name of the host to match
+ -> (l1 a) -- ^ layout to use on the matched host
+ -> (l2 a) -- ^ layout to use everywhere else
+ -> OnHost l1 l2 a
+onHost host = onHosts [host]
+
+-- | Specify one layout to use on a particular set of hosts, and
+-- another to use on all other hosts.
+onHosts :: (LayoutClass l1 a, LayoutClass l2 a)
+ => [String] -- ^ names of hosts to match
+ -> (l1 a) -- ^ layout to use on matched hosts
+ -> (l2 a) -- ^ layout to use everywhere else
+ -> OnHost l1 l2 a
+onHosts hosts l1 l2 = OnHost hosts False l1 l2
+
+-- | Specify a layout modifier to apply on a particular host; layouts
+-- on all other hosts will remain unmodified.
+modHost :: (LayoutClass l a)
+ => String -- ^ name of the host to match
+ -> (l a -> ModifiedLayout lm l a) -- ^ the modifier to apply on the matching host
+ -> l a -- ^ the base layout
+ -> OnHost (ModifiedLayout lm l) l a
+modHost host = modHosts [host]
+
+-- | Specify a layout modifier to apply on a particular set of
+-- hosts; layouts on all other hosts will remain
+-- unmodified.
+modHosts :: (LayoutClass l a)
+ => [String] -- ^ names of the hosts to match
+ -> (l a -> ModifiedLayout lm l a) -- ^ the modifier to apply on the matching hosts
+ -> l a -- ^ the base layout
+ -> OnHost (ModifiedLayout lm l) l a
+modHosts hosts f l = OnHost hosts False (f l) l
+
+-- | Structure for representing a host-specific layout along with
+-- a layout for all other hosts. We store the names of hosts
+-- to be matched, and the two layouts. We save the layout choice in
+-- the Bool, to be used to implement description.
+data OnHost l1 l2 a = OnHost [String]
+ Bool
+ (l1 a)
+ (l2 a)
+ deriving (Read, Show)
+
+instance (LayoutClass l1 a, LayoutClass l2 a, Show a) => LayoutClass (OnHost l1 l2) a where
+ runLayout (W.Workspace i p@(OnHost hosts _ lt lf) ms) r = do
+ h <- io $ getEnv "HOST"
+ if maybe False (`elemFQDN` hosts) h
+ then do (wrs, mlt') <- runLayout (W.Workspace i lt ms) r
+ return (wrs, Just $ mkNewOnHostT p mlt')
+ else do (wrs, mlt') <- runLayout (W.Workspace i lf ms) r
+ return (wrs, Just $ mkNewOnHostF p mlt')
+
+ handleMessage (OnHost hosts bool lt lf) m
+ | bool = handleMessage lt m >>= maybe (return Nothing) (\nt -> return . Just $ OnHost hosts bool nt lf)
+ | otherwise = handleMessage lf m >>= maybe (return Nothing) (\nf -> return . Just $ OnHost hosts bool lt nf)
+
+ description (OnHost _ True l1 _) = description l1
+ description (OnHost _ _ _ l2) = description l2
+
+-- | Construct new OnHost values with possibly modified layouts.
+mkNewOnHostT :: OnHost l1 l2 a -> Maybe (l1 a) -> OnHost l1 l2 a
+mkNewOnHostT (OnHost hosts _ lt lf) mlt' =
+ (\lt' -> OnHost hosts True lt' lf) $ fromMaybe lt mlt'
+
+mkNewOnHostF :: OnHost l1 l2 a -> Maybe (l2 a) -> OnHost l1 l2 a
+mkNewOnHostF (OnHost hosts _ lt lf) mlf' =
+ (\lf' -> OnHost hosts False lt lf') $ fromMaybe lf mlf'
+
+-- | 'Data.List.elem' except that if one side has a dot and the other doesn't, we truncate
+-- the one that does at the dot.
+elemFQDN :: String -> [String] -> Bool
+elemFQDN _ [] = False
+elemFQDN h0 (h:hs)
+ | h0 `eqFQDN` h = True
+ | otherwise = elemFQDN h0 hs
+
+-- | String equality, possibly truncating one side at a dot.
+eqFQDN :: String -> String -> Bool
+eqFQDN a b
+ | '.' `elem` a && '.' `elem` b = a == b
+ | '.' `elem` a = takeWhile (/= '.') a == b
+ | '.' `elem` b = a == takeWhile (/= '.') b
+ | otherwise = a == b
diff --git a/xmonad-contrib.cabal b/xmonad-contrib.cabal
index 876fc03..d481139 100644
--- a/xmonad-contrib.cabal
+++ b/xmonad-contrib.cabal
@@ -223,6 +223,7 @@ library
XMonad.Layout.Named
XMonad.Layout.NoBorders
XMonad.Layout.NoFrillsDecoration
+ XMonad.Layout.OnHost
XMonad.Layout.OneBig
XMonad.Layout.PerWorkspace
XMonad.Layout.PositionStoreFloat