diff options
| -rw-r--r-- | NEWS | 4 | ||||
| -rw-r--r-- | README | 22 | ||||
| -rw-r--r-- | src/Config.hs | 3 | ||||
| -rw-r--r-- | src/Parsers.hs | 3 | ||||
| -rw-r--r-- | src/Plugins/Kbd.hsc | 397 | ||||
| -rw-r--r-- | src/Plugins/Monitors/Volume.hs | 5 | ||||
| -rw-r--r-- | src/XUtil.hsc | 2 | ||||
| -rw-r--r-- | xmobar.cabal | 10 | 
8 files changed, 434 insertions, 12 deletions
| @@ -7,6 +7,7 @@ _New features_    - New brightness monitor, courtesy of Martin Perner.    - New DateZone plugin, for localized datetimes, also by Martin. +  - New keyboard layout monitor (Kbd).  Yes, by Martin.    - Rewrite of the event handling ([issue 53], [issue 57]), also by Martin.    - Cpu monitor now also reports `iowait` field ([issue 55]). @@ -18,6 +19,9 @@ _Bug fixes_    - Catch errors when reading battery status (Ben Boeckel).    - Compilation issues with ghc 7.x (Sergei Trofimovich).    - Fixes for CoreTemp monitor in new kernels (Norbert Zeh). +  - Fix for pulseaudio problems in volume monitor (Martin Perner). +  - Fix for parsing errors when a `Run` entry ended in an array +    (Martin).  [issue 48]: http://code.google.com/p/xmobar/issues/detail?id=48  [issue 50]: http://code.google.com/p/xmobar/issues/detail?id=50 @@ -611,10 +611,12 @@ Monitors have default aliases.      - `--lowd` _number_ Low threshold for dB. Defaults to -30.0.  - Variables that can be used with the `-t`/`--template` argument:              `volume`, `volumebar`, `dB`, `status` +- Note that `dB` might only return 0 on your system. This is known +  to happen on systems with a pulseaudio backend.  - Default template: `Vol: <volume>% <status>` -- Requires the package [alsa-mixer] installed in your system. In addition, -  to activate this plugin you must pass `--flags="with_alsa"` during -  compilation. +- Requires the package [alsa-core] and [alsa-mixer] installed in your +  system. In addition, to activate this plugin you must pass +  `--flags="with_alsa"` during compilation.  `MPD Args RefreshRate` @@ -689,6 +691,8 @@ Monitors have default aliases.    distribution can be used to set the given property from the output    of any other program or script. +[samples/xmonadpropwrite.hs script]: https://github.com/jaor/xmobar/raw/master/samples/xmonadpropwrite.hs +  `Brightness Args RefreshRate`  - Aliases to `bright` @@ -706,8 +710,18 @@ Monitors have default aliases.         Run Brightness ["-t", "<bar>"] 60 +`Kbd Opts` + +- Registers to XKB/X11-Events and output the currently active keyboard layout. +  Supports replacement of layoutnames. +- Aliases to `kbd` +- Opts is a list of tuple: +    -  first element of the tuple is the search string +    -  second element of the tuple is the corresponding replacement +- Example: + +		Run Kbd [("us(dvorak)", "DV"), ("us", "US")] -[samples/xmonadpropwrite.hs script]: https://github.com/jaor/xmobar/raw/master/samples/xmonadpropwrite.hs  ## Monitor Plugins Commands Arguments diff --git a/src/Config.hs b/src/Config.hs index 3184023..4405314 100644 --- a/src/Config.hs +++ b/src/Config.hs @@ -32,6 +32,7 @@ import Plugins.CommandReader  import Plugins.StdinReader  import Plugins.XMonadLog  import Plugins.EWMH +import Plugins.Kbd  #ifdef INOTIFY  import Plugins.Mail @@ -112,7 +113,7 @@ infixr :*:  -- the 'Runnable.Runnable' Read instance. To install a plugin just add  -- the plugin's type to the list of types (separated by ':*:') appearing in  -- this function's type signature. -runnableTypes :: Command :*: Monitors :*: Date :*: PipeReader :*: CommandReader :*: StdinReader :*: XMonadLog :*: EWMH :*: +runnableTypes :: Command :*: Monitors :*: Date :*: PipeReader :*: CommandReader :*: StdinReader :*: XMonadLog :*: EWMH :*: Kbd :*:  #ifdef INOTIFY                   Mail :*: MBox :*:  #endif diff --git a/src/Parsers.hs b/src/Parsers.hs index baaa287..f5f00a9 100644 --- a/src/Parsers.hs +++ b/src/Parsers.hs @@ -159,7 +159,8 @@ parseConfig = runParser parseConf fields "Config" . stripComments                       return ("Static {"  ++ p  ++ "}")        tillFieldEnd = staticPos <|> many (noneOf ",}\n\r") -      commandsEnd  = wrapSkip (string "]") >> oneOf "}," +      commandsEnd  = wrapSkip (string "]") >> (string "}" <|> notNextRun) +      notNextRun = do { string ","; notFollowedBy $ wrapSkip $ string "Run"; return ","}         readCommands = manyTill anyChar (try commandsEnd) >>= read' commandsErr . flip (++) "]"        strField e n = field e n . between (strDel "start" n) (strDel "end" n) . many $ noneOf "\"\n\r" diff --git a/src/Plugins/Kbd.hsc b/src/Plugins/Kbd.hsc new file mode 100644 index 0000000..d19f0c7 --- /dev/null +++ b/src/Plugins/Kbd.hsc @@ -0,0 +1,397 @@ +{-# LANGUAGE ScopedTypeVariables, ForeignFunctionInterface, MultiParamTypeClasses, DeriveDataTypeable, FlexibleInstances, PatternGuards #-} +----------------------------------------------------------------------------- +-- | +-- Module      :  Plugins.Kbd +-- Copyright   :  (c) Martin Perner +-- License     :  BSD-style (see LICENSE) +-- +-- Maintainer  :  Martin Perner <martin@perner.cc> +-- Stability   :  unstable +-- Portability :  unportable +-- +-- A keyboard layout indicator for Xmobar +-- +----------------------------------------------------------------------------- + +module Plugins.Kbd where + +import Graphics.X11.Xlib +import Graphics.X11.Xlib.Extras +import Foreign +import Foreign.C.Types (CUInt,CULong,CInt,CUShort,CChar,CUChar) +import Foreign.C.String +import Plugins +import Control.Monad (forever) +import XUtil (nextEvent') +import Data.List (isPrefixOf, findIndex) +import Data.Maybe (fromJust) + +#include <X11/XKBlib.h> +#include <X11/extensions/XKB.h> +#include <X11/extensions/XKBstr.h> + +--  +-- Definition for XkbStaceRec and getKbdLayout taken from +-- XMonad.Layout.XKBLayout +-- +data XkbStateRec = XkbStateRec { +    group :: CUChar, +    locked_group :: CUChar, +    base_group :: CUShort, +    latched_group :: CUShort, +    mods :: CUChar, +    base_mods :: CUChar, +    latched_mods :: CUChar, +    locked_mods :: CUChar, +    compat_state :: CUChar, +    grab_mods :: CUChar, +    compat_grab_mods :: CUChar, +    lookup_mods :: CUChar, +    compat_lookup_mods :: CUChar, +    ptr_buttons :: CUShort +} + +instance Storable XkbStateRec where +    sizeOf _ = (#size XkbStateRec) +    alignment _ = alignment (undefined :: CUShort) +    peek ptr = do +        r_group <- (#peek XkbStateRec, group) ptr +        r_locked_group <- (#peek XkbStateRec, locked_group) ptr +        r_base_group <- (#peek XkbStateRec, base_group) ptr +        r_latched_group <- (#peek XkbStateRec, latched_group) ptr +        r_mods <- (#peek XkbStateRec, mods) ptr +        r_base_mods <- (#peek XkbStateRec, base_mods) ptr +        r_latched_mods <- (#peek XkbStateRec, latched_mods) ptr +        r_locked_mods <- (#peek XkbStateRec, locked_mods) ptr +        r_compat_state <- (#peek XkbStateRec, compat_state) ptr +        r_grab_mods <- (#peek XkbStateRec, grab_mods) ptr +        r_compat_grab_mods <- (#peek XkbStateRec, compat_grab_mods) ptr +        r_lookup_mods <- (#peek XkbStateRec, lookup_mods) ptr +        r_compat_lookup_mods <- (#peek XkbStateRec, compat_lookup_mods) ptr +        r_ptr_buttons <- (#peek XkbStateRec, ptr_buttons) ptr +        return XkbStateRec { +            group = r_group, +            locked_group = r_locked_group, +            base_group = r_base_group, +            latched_group = r_latched_group, +            mods = r_mods, +            base_mods = r_base_mods, +            latched_mods = r_latched_mods, +            locked_mods = r_locked_mods, +            compat_state = r_compat_state, +            grab_mods = r_grab_mods, +            compat_grab_mods = r_compat_grab_mods, +            lookup_mods = r_lookup_mods, +            compat_lookup_mods = r_compat_lookup_mods, +            ptr_buttons = r_ptr_buttons +        } + +foreign import ccall unsafe "X11/XKBlib.h XkbGetState" +    xkbGetState :: Display -> CUInt -> Ptr XkbStateRec -> IO CInt + + +getKbdLayout :: Display -> IO Int +getKbdLayout d = alloca $ \stRecPtr -> do +    xkbGetState d 0x100 stRecPtr +    st <- peek stRecPtr +    return $ fromIntegral (group st) + +-- +-- +-- + +data XkbKeyNameRec = XkbKeyNameRec { +	name :: Ptr CChar -- array +} + +-- +-- the t_ before alias is just because of name collisions +-- +data XkbKeyAliasRec = XkbKeyAliasRec { +	real  :: Ptr CChar, -- array +	t_alias :: Ptr CChar  -- array +}  + +-- +-- the t_ before geometry is just because of name collisions +-- +data XkbNamesRec = XkbNamesRec { +    keycodes :: Atom, +    t_geometry :: Atom, +    symbols :: Atom, +    types :: Atom, +    compat :: Atom, +    vmods :: Ptr Atom, +    indicators :: Ptr Atom, -- array +    groups :: Ptr Atom, -- array +    keys :: Ptr XkbKeyNameRec, +    key_aliases :: Ptr CChar, -- dont care XkbKeyAliasRec, +    radio_groups :: Ptr Atom, +    phys_symbols :: Atom, +    num_keys :: CUChar, +    num_key_aliases :: CUChar, +    num_rg :: CUShort +} + +-- +-- the t_ before map, indicators and compat are just because of name collisions +-- +data XkbDescRec = XkbDescRec { +    t_dpy :: Ptr CChar, -- struct _XDisplay* ; don't care +    flags :: CUShort, +    device_spec :: CUShort, +    min_key_code :: KeyCode, +    max_key_code :: KeyCode, +    ctrls :: Ptr CChar, -- XkbControlsPtr ;  dont' care +    server :: Ptr CChar, -- XkbServerMapPtr ;  dont' care +    t_map :: Ptr CChar, --XkbClientMapPtr ;  dont' care +    t_indicators :: Ptr CChar, -- XkbIndicatorPtr ;  dont' care +	names :: Ptr XkbNamesRec, -- array +    t_compat :: Ptr CChar, -- XkbCompatMap ;  dont' care +    geom :: Ptr CChar -- XkbGeometryPtr ;  dont' care + +} + +instance Storable XkbKeyNameRec where +    sizeOf _ = (#size XkbKeyNameRec) +    alignment _ = alignment (undefined :: CUShort) +    peek ptr = do +        r_name <- (#peek XkbKeyNameRec, name) ptr + +        return XkbKeyNameRec { +            name = r_name +        } + +instance Storable XkbKeyAliasRec where +    sizeOf _ = (#size XkbKeyAliasRec) +    alignment _ = alignment (undefined :: CUShort) +    peek ptr = do +        r_real <- (#peek XkbKeyAliasRec, real) ptr +        r_alias <- (#peek XkbKeyAliasRec, alias) ptr + +        return XkbKeyAliasRec { +            real = r_real, +            t_alias = r_alias +        } + +instance Storable XkbNamesRec where +    sizeOf _ = (#size XkbNamesRec) +    alignment _ = alignment (undefined :: CUShort) +    peek ptr = do +        r_keycodes <- (#peek XkbNamesRec, keycodes) ptr +        r_geometry <- (#peek XkbNamesRec, geometry) ptr +        r_symbols <- (#peek XkbNamesRec, symbols ) ptr +        r_types <- (#peek XkbNamesRec, types ) ptr +        r_compat <- (#peek XkbNamesRec, compat ) ptr   +        r_vmods <- (#peek XkbNamesRec,  vmods ) ptr +        r_indicators <- (#peek XkbNamesRec, indicators ) ptr   +        r_groups <- (#peek XkbNamesRec, groups ) ptr  +        r_keys <- (#peek XkbNamesRec, keys ) ptr    +        r_key_aliases <- (#peek XkbNamesRec, key_aliases  ) ptr +        r_radio_groups <- (#peek XkbNamesRec, radio_groups  ) ptr  +        r_phys_symbols <- (#peek XkbNamesRec, phys_symbols ) ptr   +        r_num_keys <- (#peek XkbNamesRec,num_keys  ) ptr +        r_num_key_aliases <- (#peek XkbNamesRec, num_key_aliases  ) ptr +        r_num_rg <- (#peek XkbNamesRec, num_rg ) ptr + +        return XkbNamesRec { +            keycodes = r_keycodes, +            t_geometry = r_geometry, +            symbols = r_symbols, +            types = r_types, +            compat = r_compat, +            vmods = r_vmods, +            indicators = r_indicators, +            groups = r_groups, +            keys = r_keys, +            key_aliases = r_key_aliases, +            radio_groups = r_radio_groups, +            phys_symbols = r_phys_symbols, +            num_keys = r_num_keys, +            num_key_aliases = r_num_key_aliases, +            num_rg = r_num_rg +       } + +instance Storable XkbDescRec where +    sizeOf _ = (#size XkbDescRec) +    alignment _ = alignment (undefined :: CUShort) +    peek ptr = do +        r_dpy <- (#peek XkbDescRec, dpy) ptr +        r_flags <- (#peek XkbDescRec, flags) ptr +        r_device_spec <- (#peek XkbDescRec, device_spec) ptr +        r_min_key_code <- (#peek XkbDescRec, min_key_code) ptr +        r_max_key_code <- (#peek XkbDescRec, max_key_code) ptr +        r_ctrls <- (#peek XkbDescRec, ctrls) ptr +        r_server <- (#peek XkbDescRec, server) ptr +        r_map <- (#peek XkbDescRec, map) ptr +        r_indicators <- (#peek XkbDescRec, indicators) ptr +        r_names <- (#peek XkbDescRec, names) ptr +        r_compat <- (#peek XkbDescRec, compat) ptr +        r_geom <- (#peek XkbDescRec, geom) ptr + +        return XkbDescRec { +            t_dpy = r_dpy, +            flags = r_flags, +            device_spec = r_device_spec, +            min_key_code = r_min_key_code, +            max_key_code = r_max_key_code, +            ctrls = r_ctrls, +            server = r_server, +            t_map = r_map, +            t_indicators = r_indicators, +            names = r_names, +            t_compat = r_compat, +            geom = r_geom +        } + +-- +-- C bindings +-- + +foreign import ccall unsafe "X11/XKBlib.h XkbAllocKeyboard" +    xkbAllocKeyboard :: IO (Ptr XkbDescRec) + +foreign import ccall unsafe "X11/XKBlib.h XkbGetNames" +    xkbGetNames :: Display -> CUInt -> (Ptr XkbDescRec)  -> IO Status + +foreign import ccall unsafe "X11/XKBlib.h XGetAtomName" +    xGetAtomName :: Display -> Atom -> IO CString + +foreign import ccall unsafe "X11/XKBlib.h XkbFreeNames" +    xkbFreeNames :: (Ptr XkbDescRec) -> CUInt -> CInt -> IO () + +foreign import ccall unsafe "X11/XKBlib.h XkbFreeKeyboard" +    xkbFreeKeyboard :: (Ptr XkbDescRec) -> CUInt -> CInt -> IO () + +foreign import ccall unsafe "X11/XKBlib.h XkbSelectEventDetails" +	xkbSelectEventDetails :: Display -> CUInt -> CUInt -> CULong -> CULong -> IO CUInt + +foreign import ccall unsafe "X11/XKBlib.h XkbSelectEvents" +	xkbSelectEvents :: Display -> CUInt -> CUInt -> CUInt -> IO CUInt + + +xkbUseCoreKbd :: CUInt +xkbUseCoreKbd = #const XkbUseCoreKbd + +xkbStateNotify :: CUInt +xkbStateNotify = #const XkbStateNotify + +xkbMapNotify :: CUInt +xkbMapNotify = #const XkbMapNotify + +xkbMapNotifyMask :: CUInt +xkbMapNotifyMask = #const XkbMapNotifyMask + +xkbNewKeyboardNotifyMask :: CUInt +xkbNewKeyboardNotifyMask  = #const XkbNewKeyboardNotifyMask + +xkbAllStateComponentsMask :: CULong +xkbAllStateComponentsMask = #const XkbAllStateComponentsMask + +xkbGroupStateMask :: CULong +xkbGroupStateMask = #const XkbGroupStateMask + +xkbSymbolsNameMask :: CUInt +xkbSymbolsNameMask = #const XkbSymbolsNameMask + +xkbGroupNamesMask :: CUInt +xkbGroupNamesMask = #const XkbGroupNamesMask + +type KbdOpts = [(String, String)] + +-- gets the layout string +getLayoutStr :: Display -> IO String +getLayoutStr dpy =  do +        kbdDescPtr <- xkbAllocKeyboard +        status <- xkbGetNames dpy xkbSymbolsNameMask kbdDescPtr +        str <- getLayoutStr' status dpy kbdDescPtr +        xkbFreeNames kbdDescPtr xkbGroupNamesMask 1 +        xkbFreeKeyboard kbdDescPtr 0 1 +        return str + +getLayoutStr' :: Status -> Display -> (Ptr XkbDescRec) -> IO String +getLayoutStr' st dpy kbdDescPtr = +        if st == 0 then -- Success +            do +            kbdDesc <- peek kbdDescPtr +            nameArray <- peek (names kbdDesc) +            atom <- xGetAtomName dpy (symbols nameArray) +            str <- peekCString atom +            return str +        else -- Behaviour on error +            do +                return "Error while requesting layout!" + + +-- 'Bad' prefixes of layouts +noLaySymbols :: [String] +noLaySymbols = ["group", "inet", "ctr", "pc", "ctrl"] + + +-- splits the layout string into the actual layouts +splitLayout :: String -> [String] +splitLayout s = splitLayout' noLaySymbols $ split s '+'  + +splitLayout' :: [String] ->  [String] -> [String] +--                  end of recursion, remove empty strings +splitLayout' [] s = map (takeWhile (\x -> x /= ':')) $ filter (\x -> length x > 0) s +--                    remove current string if it has a 'bad' prefix +splitLayout' bad s  = splitLayout' (tail bad) [x | x <- s, not $ isPrefixOf (head bad) x] + +-- split String at each Char +split :: String -> Char -> [String] +split [] _ = [""] +split (c:cs) delim +    | c == delim = "" : rest +    | otherwise = (c : head rest) : tail rest +        where +            rest = split cs delim + +-- replaces input string if on search list (exact match) with corresponding +-- element on replacement list. +--  +-- if not found, return string unchanged +searchReplaceLayout :: KbdOpts -> String -> String +searchReplaceLayout opts s = let c = findIndex (\x -> fst x == s) opts in +    case c of +        Nothing -> s +        x -> let i = (fromJust x) in +            snd $ opts!!i + +-- returns the active layout +getKbdLay :: Display -> KbdOpts -> IO String +getKbdLay dpy opts = do +        lay <- getLayoutStr dpy +        curLay <- getKbdLayout dpy +        return $ searchReplaceLayout opts $ (splitLayout lay)!!(curLay) + + + +data Kbd = Kbd [(String, String)] +	deriving (Read, Show) + +instance Exec Kbd where +	alias (Kbd _) = "kbd" +	start (Kbd opts) cb = do + +        dpy <- openDisplay "" + +        -- initial set of layout +        cb =<< (getKbdLay dpy opts) + +        -- enable listing for +        -- group changes +        _ <- xkbSelectEventDetails dpy xkbUseCoreKbd xkbStateNotify xkbAllStateComponentsMask xkbGroupStateMask +        -- layout/geometry changes +        _ <- xkbSelectEvents dpy  xkbUseCoreKbd xkbNewKeyboardNotifyMask xkbNewKeyboardNotifyMask + +        allocaXEvent $ \e -> forever $ do +            nextEvent' dpy e +            _ <- getEvent e +            cb =<< (getKbdLay dpy opts) + +        closeDisplay dpy +        return () + +-- vim:ft=haskell:ts=4:shiftwidth=4:softtabstop=4:expandtab:foldlevel=20: + diff --git a/src/Plugins/Monitors/Volume.hs b/src/Plugins/Monitors/Volume.hs index 50a6ed4..3e3a8b9 100644 --- a/src/Plugins/Monitors/Volume.hs +++ b/src/Plugins/Monitors/Volume.hs @@ -14,10 +14,12 @@  module Plugins.Monitors.Volume (runVolume, volumeConfig) where +import Prelude hiding ( catch )  import Control.Monad ( liftM, mplus )  import Data.Maybe  import Plugins.Monitors.Common  import Sound.ALSA.Mixer +import Sound.ALSA.Exception ( catch )  import System.Console.GetOpt  volumeConfig :: IO MConfig @@ -115,7 +117,8 @@ runVolume mixerName controlName argv = do          maybeNA = maybe (return "N/A")      (lo, hi) <- io $ getRange volumeControl      val <- io $ getChannel FrontLeft $ value volumeControl -    db <- io $ getChannel FrontLeft $ dB volumeControl +    db <- io $ catch (getChannel FrontLeft $ dB volumeControl) +                     (\_ -> return $ Just 0)      sw <- io $ getChannel FrontLeft switchControl      p <- maybeNA (formatVol lo hi) val      b <- maybeNA (formatVolBar lo hi) val diff --git a/src/XUtil.hsc b/src/XUtil.hsc index cb0c89a..ea051f8 100644 --- a/src/XUtil.hsc +++ b/src/XUtil.hsc @@ -2,7 +2,7 @@  -----------------------------------------------------------------------------  -- |  -- Module      :  XUtil --- Copyright   :  (C) 2007 Andrea Rossato +-- Copyright   :  (C) 2007, 2011 Andrea Rossato  -- License     :  BSD3  --  -- Maintainer  :  andrea.rossato@unitn.it diff --git a/xmobar.cabal b/xmobar.cabal index e40494f..b0afc26 100644 --- a/xmobar.cabal +++ b/xmobar.cabal @@ -68,10 +68,11 @@ executable xmobar        Xmobar, Config, Parsers, Commands, XUtil, StatFS, Runnable,        Plugins, Plugins.CommandReader, Plugins.Date, Plugins.EWMH,        Plugins.PipeReader, Plugins.StdinReader, Plugins.XMonadLog, -      Plugins.Utils, Plugins.Monitors, Plugins.Monitors.Batt, -      Plugins.Monitors.Common, Plugins.Monitors.CoreCommon, -      Plugins.Monitors.CoreTemp, Plugins.Monitors.CpuFreq, -      Plugins.Monitors.Cpu, Plugins.Monitors.Disk, Plugins.Monitors.Mem, +      Plugins.Utils, Plugins.Kbd, Plugins.Monitors, +      Plugins.Monitors.Batt, Plugins.Monitors.Common, +      Plugins.Monitors.CoreCommon, Plugins.Monitors.CoreTemp, +      Plugins.Monitors.CpuFreq, Plugins.Monitors.Cpu, +      Plugins.Monitors.Disk, Plugins.Monitors.Mem,        Plugins.Monitors.MultiCpu, Plugins.Monitors.Net,        Plugins.Monitors.Swap, Plugins.Monitors.Thermal,        Plugins.Monitors.ThermalZone, Plugins.Monitors.Top, @@ -123,6 +124,7 @@ executable xmobar      if flag(with_alsa) || flag(all_extensions)         build-depends: alsa-mixer == 0.1.* +       build-depends: alsa-core == 0.5.*         other-modules: Plugins.Monitors.Volume         cpp-options: -DALSA | 
