mirror of
https://github.com/xmonad/xmonad-contrib.git
synced 2025-05-19 11:30:22 -07:00
156 lines
6.9 KiB
Haskell
156 lines
6.9 KiB
Haskell
{-# 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
|