|
| 1 | +{-# LANGUAGE BlockArguments #-} |
| 2 | +{-# LANGUAGE NoOverloadedStrings #-} |
| 3 | +{-# LANGUAGE LambdaCase #-} |
| 4 | +{-# LANGUAGE ViewPatterns #-} |
| 5 | +module XMonad where |
| 6 | + |
| 7 | +-- import Control.Exception (IOException, try) |
| 8 | +import System.Process |
| 9 | +import System.Posix.Process (getProcessID) |
| 10 | +-- import Data.Maybe (listToMaybe) |
| 11 | +import Data.Functor |
| 12 | +import System.Exit |
| 13 | +import Data.List |
| 14 | + |
| 15 | +import Imports |
| 16 | + |
| 17 | +newtype PID = PID String |
| 18 | +newtype XWindowId = XWindowId String |
| 19 | +newtype Tag = Tag String |
| 20 | + |
| 21 | +instance IsString Tag where |
| 22 | + fromString = Tag |
| 23 | + |
| 24 | +-- Get the parent PID of a process |
| 25 | +getParentPid :: PID -> IO (Maybe PID) |
| 26 | +getParentPid (PID pid) = readProcessWithExitCode "ps" ["-o", "ppid=", "-p", pid] "" <&> \ case |
| 27 | + (_, result, _) -> case strip result of |
| 28 | + "" -> Nothing |
| 29 | + ppid -> Just (PID ppid) |
| 30 | + |
| 31 | +getWindowIdForPid :: PID -> IO (Maybe XWindowId) |
| 32 | +getWindowIdForPid (PID pid) = do |
| 33 | + windowIds <- filter (isPrefixOf "0x") . words <$> readProcess "xprop" ["-root", "_NET_CLIENT_LIST"] "" |
| 34 | + findWindow windowIds |
| 35 | + where |
| 36 | + findWindow :: [String] -> IO (Maybe XWindowId) |
| 37 | + findWindow = \ case |
| 38 | + [] -> return Nothing |
| 39 | + wid : rest -> do |
| 40 | + wmPidOutput <- readProcess "xprop" ["-id", wid, "_NET_WM_PID"] "" |
| 41 | + |
| 42 | + case listToMaybe (filter (all (`elem` "0123456789")) (words wmPidOutput)) of |
| 43 | + Just wmPid | wmPid == pid -> return (Just $ XWindowId wid) |
| 44 | + _ -> findWindow rest |
| 45 | + |
| 46 | +-- Recursively find the first ancestor PID that is an X window |
| 47 | +findAncestorWindowId :: PID -> IO (Maybe XWindowId) |
| 48 | +findAncestorWindowId pid = do |
| 49 | + windowId <- getWindowIdForPid pid |
| 50 | + case windowId of |
| 51 | + Just wid -> return (Just wid) |
| 52 | + Nothing -> do |
| 53 | + parentPid <- getParentPid pid |
| 54 | + case parentPid of |
| 55 | + Just ppid -> findAncestorWindowId ppid |
| 56 | + Nothing -> return Nothing |
| 57 | + |
| 58 | +addTag :: Tag -> XWindowId -> IO () |
| 59 | +addTag (Tag name) (XWindowId wid) = do |
| 60 | + result <- readProcess "xprop" ["-id", wid, "_XMONAD_TAGS"] "" |
| 61 | + |
| 62 | + let |
| 63 | + tags :: String |
| 64 | + tags = if "not found" `elem` words result then "" else extractTags result |
| 65 | + |
| 66 | + newTags :: String |
| 67 | + newTags = if null tags then name else tags <> " " <> name |
| 68 | + |
| 69 | + callProcess "xprop" ["-id", wid, "-f", "_XMONAD_TAGS", "8s", "-set", "_XMONAD_TAGS", newTags] |
| 70 | + where |
| 71 | + extractTags :: String -> String |
| 72 | + extractTags = unwords . drop 1 . words . last . lines |
| 73 | + |
| 74 | +tagSelfWith :: Tag -> IO () |
| 75 | +tagSelfWith name = do |
| 76 | + pid <- PID . show <$> getProcessID |
| 77 | + result <- findAncestorWindowId pid |
| 78 | + case result of |
| 79 | + Just wid -> addTag name wid |
| 80 | + Nothing -> exitFailure |
0 commit comments