From a8d41df92bc2712e2e2fcd85bdb62a9f2ea9754f Mon Sep 17 00:00:00 2001
From: Sibi Prabakaran <sibi@psibi.in>
Date: Mon, 1 Jul 2019 11:46:24 +0530
Subject: [PATCH] Added allApplications to XMonad.Prompt.Window

---
 CHANGES.md                      | 16 +++++++++++++---
 XMonad/Actions/WindowBringer.hs | 16 ++++++++++++++--
 XMonad/Prompt/Window.hs         |  5 +++++
 XMonad/Util/NamedWindows.hs     | 15 +++++++++++++++
 4 files changed, 47 insertions(+), 5 deletions(-)

diff --git a/CHANGES.md b/CHANGES.md
index 9ed2b1ab..c56f9e48 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -25,11 +25,11 @@
 ### New Modules
 
   * `XMonad.Layout.TallMastersCombo`
-    A layout combinator that support Shrink, Expand, and IncMasterN just as 
-    the 'Tall' layout, and also support operations of two master windows: 
+    A layout combinator that support Shrink, Expand, and IncMasterN just as
+    the 'Tall' layout, and also support operations of two master windows:
     a main master, which is the original master window;
     a sub master, the first window of the second pane.
-    This combinator can be nested, and has a good support for using 
+    This combinator can be nested, and has a good support for using
     'XMonad.Layout.Tabbed' as a sublayout.
 
   * `XMonad.Layout.TwoPanePersistent`
@@ -79,6 +79,16 @@
     Made password prompts traverse symlinks when gathering password names for
     autocomplete.
 
+* `XMonad.Prompt.Window`
+
+    Added 'allApplications' function which maps application executable
+    names to it's underlying window.
+
+* `XMonad.Prompt.WindowBringer`
+
+    Added 'windowApMap' function which maps application executable
+    names to it's underlying window.
+
   * `XMonad.Actions.DynamicProjects`
 
     Make the input directory read from the prompt in `DynamicProjects`
diff --git a/XMonad/Actions/WindowBringer.hs b/XMonad/Actions/WindowBringer.hs
index 46593bb5..49aa3226 100644
--- a/XMonad/Actions/WindowBringer.hs
+++ b/XMonad/Actions/WindowBringer.hs
@@ -21,7 +21,7 @@ module XMonad.Actions.WindowBringer (
                     WindowBringerConfig(..),
                     gotoMenu, gotoMenuConfig, gotoMenu', gotoMenuArgs, gotoMenuArgs',
                     bringMenu, bringMenuConfig, bringMenu', bringMenuArgs, bringMenuArgs',
-                    windowMap, windowMap', bringWindow, actionMenu
+                    windowMap, windowAppMap, windowMap', bringWindow, actionMenu
                    ) where
 
 import Control.Applicative((<$>))
@@ -31,7 +31,7 @@ import qualified XMonad.StackSet as W
 import XMonad
 import qualified XMonad as X
 import XMonad.Util.Dmenu (menuMapArgs)
-import XMonad.Util.NamedWindows (getName)
+import XMonad.Util.NamedWindows (getName, getNameWMClass)
 
 -- $usage
 --
@@ -137,6 +137,10 @@ actionMenu WindowBringerConfig{ menuCommand = cmd
 windowMap :: X (M.Map String Window)
 windowMap = windowMap' decorateName
 
+-- | A map from application executable names to Windows.
+windowAppMap :: X (M.Map String Window)
+windowAppMap = windowMap' decorateAppName
+
 -- | A map from window names to Windows, given a windowTitler function.
 windowMap' :: (X.WindowSpace -> Window -> X String) -> X (M.Map String Window)
 windowMap' titler = do
@@ -152,3 +156,11 @@ decorateName :: X.WindowSpace -> Window -> X String
 decorateName ws w = do
   name <- show <$> getName w
   return $ name ++ " [" ++ W.tag ws ++ "]"
+
+-- | Returns the window name as will be listed in dmenu.  This will
+-- return the executable name of the window along with it's workspace
+-- ID.
+decorateAppName :: X.WindowSpace -> Window -> X String
+decorateAppName ws w = do
+  name <- show <$> getNameWMClass w
+  return $ name ++ " [" ++ W.tag ws ++ "]"
diff --git a/XMonad/Prompt/Window.hs b/XMonad/Prompt/Window.hs
index d841f463..9bf5fe57 100644
--- a/XMonad/Prompt/Window.hs
+++ b/XMonad/Prompt/Window.hs
@@ -22,6 +22,7 @@ module XMonad.Prompt.Window
     windowPrompt,
     windowMultiPrompt,
     allWindows,
+    allApplications,
     wsWindows,
     XWindowMap,
 
@@ -120,6 +121,10 @@ windowPromptBringCopy c = windowPrompt c BringCopy windowMap
 allWindows :: XWindowMap
 allWindows = windowMap
 
+-- | A helper to get the map of all applications
+allApplications :: XWindowMap
+allApplications = windowAppMap
+
 -- | A helper to get the map of windows of the current workspace.
 wsWindows :: XWindowMap
 wsWindows = withWindowSet (return . W.index) >>= winmap
diff --git a/XMonad/Util/NamedWindows.hs b/XMonad/Util/NamedWindows.hs
index 61176d5c..3dfaa43e 100644
--- a/XMonad/Util/NamedWindows.hs
+++ b/XMonad/Util/NamedWindows.hs
@@ -18,6 +18,7 @@ module XMonad.Util.NamedWindows (
                                    -- $usage
                                    NamedWindow,
                                    getName,
+                                   getNameWMClass,
                                    withNamedWindow,
                                    unName
                                   ) where
@@ -55,6 +56,20 @@ getName w = withDisplay $ \d -> do
 
     io $ getIt `E.catch` \(SomeException _) ->  ((`NW` w) . resName) `fmap` getClassHint d w
 
+-- | Get 'NamedWindow' using 'wM_CLASS'
+getNameWMClass :: Window -> X NamedWindow
+getNameWMClass w =
+  withDisplay $ \d
+    -- TODO, this code is ugly and convoluted -- clean it up
+   -> do
+    let getIt = bracket getProp (xFree . tp_value) (fmap (`NW` w) . copy)
+        getProp = getTextProperty d w wM_CLASS
+        copy prop =
+          fromMaybe "" . listToMaybe <$> wcTextPropertyToTextList d prop
+    io $
+      getIt `E.catch` \(SomeException _) ->
+        ((`NW` w) . resName) `fmap` getClassHint d w
+
 unName :: NamedWindow -> Window
 unName (NW _ w) = w