Actions.SpawnOn: make spawnOn more reliable on Linux, by tracking children across fork

This commit is contained in:
Kosyrev Serge
2016-02-28 16:41:59 +03:00
parent 65ac029636
commit 33237f47f7
2 changed files with 41 additions and 3 deletions

View File

@@ -41,6 +41,11 @@
- The vertical centring of text in each cell has been improved. - The vertical centring of text in each cell has been improved.
* `XMonad.Actions.SpawnOn`
- Bind windows spawns by child processes of the original window to the same
workspace as the original window.
* `XMonad.Util.WindowProperties` * `XMonad.Util.WindowProperties`
- Added the ability to test if a window has a tag from - Added the ability to test if a window has a tag from

View File

@@ -28,8 +28,14 @@ module XMonad.Actions.SpawnOn (
shellPromptOn shellPromptOn
) where ) where
import Control.Exception (tryJust)
import Control.Monad (guard)
import Data.List (isInfixOf) import Data.List (isInfixOf)
import Data.Maybe (isJust)
import System.IO.Error (isDoesNotExistError)
import System.IO.Unsafe (unsafePerformIO)
import System.Posix.Types (ProcessID) import System.Posix.Types (ProcessID)
import Text.Printf (printf)
import XMonad import XMonad
import qualified XMonad.StackSet as W import qualified XMonad.StackSet as W
@@ -68,6 +74,25 @@ instance ExtensionClass Spawner where
initialValue = Spawner [] initialValue = Spawner []
getPPIDOf :: ProcessID -> Maybe ProcessID
getPPIDOf pid =
case unsafePerformIO . tryJust (guard . isDoesNotExistError) . readFile . printf "/proc/%d/stat" $ toInteger pid of
Left _ -> Nothing
Right contents -> case lines contents of
[] -> Nothing
first : _ -> case words first of
_ : _ : _ : ppid : _ -> Just $ fromIntegral (read ppid :: Int)
_ -> Nothing
getPPIDChain :: ProcessID -> [ProcessID]
getPPIDChain pid' = ppid_chain pid' []
where ppid_chain pid acc =
if pid == 0
then acc
else case getPPIDOf pid of
Nothing -> acc
Just ppid -> ppid_chain ppid (ppid : acc)
-- | Get the current Spawner or create one if it doesn't exist. -- | Get the current Spawner or create one if it doesn't exist.
modifySpawner :: ([(ProcessID, ManageHook)] -> [(ProcessID, ManageHook)]) -> X () modifySpawner :: ([(ProcessID, ManageHook)] -> [(ProcessID, ManageHook)]) -> X ()
modifySpawner f = XS.modify (Spawner . f . pidsRef) modifySpawner f = XS.modify (Spawner . f . pidsRef)
@@ -83,9 +108,17 @@ manageSpawnWithGC :: ([(ProcessID, ManageHook)] -> X [(ProcessID, ManageHook)])
manageSpawnWithGC garbageCollect = do manageSpawnWithGC garbageCollect = do
Spawner pids <- liftX XS.get Spawner pids <- liftX XS.get
mp <- pid mp <- pid
case flip lookup pids =<< mp of let ppid_chain = case mp of
Nothing -> idHook Just winpid -> winpid : getPPIDChain winpid
Just mh -> do Nothing -> []
known_window_handlers = [ mh
| ppid <- ppid_chain
, let mpid = lookup ppid pids
, isJust mpid
, let (Just mh) = mpid ]
case known_window_handlers of
[] -> idHook
(mh:_) -> do
whenJust mp $ \p -> liftX $ do whenJust mp $ \p -> liftX $ do
ps <- XS.gets pidsRef ps <- XS.gets pidsRef
XS.put . Spawner =<< garbageCollect (filter ((/= p) . fst) ps) XS.put . Spawner =<< garbageCollect (filter ((/= p) . fst) ps)