-----------------------------------------------------------------------------
-- |
-- Module      :  XMonad.Util.Dmenu
-- Description :  Convenient bindings to dmenu.
-- Copyright   :  (c) Spencer Janssen <spencerjanssen@gmail.com>
-- License     :  BSD-style (see LICENSE)
--
-- Maintainer  :  Spencer Janssen <spencerjanssen@gmail.com>
-- Stability   :  unstable
-- Portability :  unportable
--
-- A convenient binding to dmenu.
--
-- Requires the process-1.0 package
--
-----------------------------------------------------------------------------

module XMonad.Util.Dmenu (
                -- * Usage
                -- $usage
                dmenu, dmenuXinerama, dmenuMap, menu, menuArgs, menuMap, menuMapArgs
               ) where

import XMonad
import qualified XMonad.StackSet as W
import qualified Data.Map as M
import XMonad.Util.Run

-- $usage
-- You can use this module with the following in your Config.hs file:
--
-- > import XMonad.Util.Dmenu
--
-- These functions block xmonad's event loop until dmenu exits; this means that
-- programs will not be able to open new windows and you will not be able to
-- change workspaces or input focus until you have responded to the prompt one
-- way or another.

-- %import XMonad.Util.Dmenu

-- | Starts dmenu on the current screen. Requires this patch to dmenu:
-- <http://www.jcreigh.com/dmenu/dmenu-3.2-xinerama.patch>
dmenuXinerama :: [String] -> X String
dmenuXinerama :: [String] -> X String
dmenuXinerama [String]
opts = do
    Int
curscreen <-
      ScreenId -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral (ScreenId -> Int)
-> (StackSet String (Layout Window) Window ScreenId ScreenDetail
    -> ScreenId)
-> StackSet String (Layout Window) Window ScreenId ScreenDetail
-> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Screen String (Layout Window) Window ScreenId ScreenDetail
-> ScreenId
forall i l a sid sd. Screen i l a sid sd -> sid
W.screen (Screen String (Layout Window) Window ScreenId ScreenDetail
 -> ScreenId)
-> (StackSet String (Layout Window) Window ScreenId ScreenDetail
    -> Screen String (Layout Window) Window ScreenId ScreenDetail)
-> StackSet String (Layout Window) Window ScreenId ScreenDetail
-> ScreenId
forall b c a. (b -> c) -> (a -> b) -> a -> c
. StackSet String (Layout Window) Window ScreenId ScreenDetail
-> Screen String (Layout Window) Window ScreenId ScreenDetail
forall i l a sid sd. StackSet i l a sid sd -> Screen i l a sid sd
W.current (StackSet String (Layout Window) Window ScreenId ScreenDetail
 -> Int)
-> X (StackSet String (Layout Window) Window ScreenId ScreenDetail)
-> X Int
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (XState
 -> StackSet String (Layout Window) Window ScreenId ScreenDetail)
-> X (StackSet String (Layout Window) Window ScreenId ScreenDetail)
forall s (m :: * -> *) a. MonadState s m => (s -> a) -> m a
gets XState
-> StackSet String (Layout Window) Window ScreenId ScreenDetail
windowset :: X Int
    String
_ <-
      String -> [String] -> String -> X String
forall (m :: * -> *).
MonadIO m =>
String -> [String] -> String -> m String
runProcessWithInput String
"dmenu" [String
"-xs", Int -> String
forall a. Show a => a -> String
show (Int
curscreenInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1)] ([String] -> String
unlines [String]
opts)
    String -> [String] -> [String] -> X String
forall (m :: * -> *).
MonadIO m =>
String -> [String] -> [String] -> m String
menuArgs String
"dmenu" [String
"-xs", Int -> String
forall a. Show a => a -> String
show (Int
curscreenInt -> Int -> Int
forall a. Num a => a -> a -> a
+Int
1)] [String]
opts

-- | Run dmenu to select an option from a list.
dmenu :: MonadIO m => [String] -> m String
dmenu :: forall (m :: * -> *). MonadIO m => [String] -> m String
dmenu = String -> [String] -> m String
forall (m :: * -> *). MonadIO m => String -> [String] -> m String
menu String
"dmenu"

-- | like 'dmenu' but also takes the command to run.
menu :: MonadIO m => String -> [String] -> m String
menu :: forall (m :: * -> *). MonadIO m => String -> [String] -> m String
menu String
menuCmd = String -> [String] -> [String] -> m String
forall (m :: * -> *).
MonadIO m =>
String -> [String] -> [String] -> m String
menuArgs String
menuCmd []

-- | Like 'menu' but also takes a list of command line arguments.
menuArgs :: MonadIO m => String -> [String] -> [String] -> m String
menuArgs :: forall (m :: * -> *).
MonadIO m =>
String -> [String] -> [String] -> m String
menuArgs String
menuCmd [String]
args [String]
opts = (Char -> Bool) -> String -> String
forall a. (a -> Bool) -> [a] -> [a]
filter (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/=Char
'\n') (String -> String) -> m String -> m String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>
  String -> [String] -> String -> m String
forall (m :: * -> *).
MonadIO m =>
String -> [String] -> String -> m String
runProcessWithInput String
menuCmd [String]
args ([String] -> String
unlines [String]
opts)

-- | Like 'dmenuMap' but also takes the command to run.
menuMap :: MonadIO m => String -> M.Map String a -> m (Maybe a)
menuMap :: forall (m :: * -> *) a.
MonadIO m =>
String -> Map String a -> m (Maybe a)
menuMap String
menuCmd = String -> [String] -> Map String a -> m (Maybe a)
forall (m :: * -> *) a.
MonadIO m =>
String -> [String] -> Map String a -> m (Maybe a)
menuMapArgs String
menuCmd []

-- | Like 'menuMap' but also takes a list of command line arguments.
menuMapArgs :: MonadIO m => String -> [String] -> M.Map String a ->
               m (Maybe a)
menuMapArgs :: forall (m :: * -> *) a.
MonadIO m =>
String -> [String] -> Map String a -> m (Maybe a)
menuMapArgs String
menuCmd [String]
args Map String a
selectionMap = do
  String
selection <- [String] -> m String
menuFunction (Map String a -> [String]
forall k a. Map k a -> [k]
M.keys Map String a
selectionMap)
  Maybe a -> m (Maybe a)
forall a. a -> m a
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe a -> m (Maybe a)) -> Maybe a -> m (Maybe a)
forall a b. (a -> b) -> a -> b
$ String -> Map String a -> Maybe a
forall k a. Ord k => k -> Map k a -> Maybe a
M.lookup String
selection Map String a
selectionMap
      where
        menuFunction :: [String] -> m String
menuFunction = String -> [String] -> [String] -> m String
forall (m :: * -> *).
MonadIO m =>
String -> [String] -> [String] -> m String
menuArgs String
menuCmd [String]
args

-- | Run dmenu to select an entry from a map based on the key.
dmenuMap :: MonadIO m => M.Map String a -> m (Maybe a)
dmenuMap :: forall (m :: * -> *) a. MonadIO m => Map String a -> m (Maybe a)
dmenuMap = String -> Map String a -> m (Maybe a)
forall (m :: * -> *) a.
MonadIO m =>
String -> Map String a -> m (Maybe a)
menuMap String
"dmenu"