    --
    -- Copyright (c) 2012 Mark Dittmer - http://www.markdittmer.org
    -- Developed for a Google Summer of Code project - http://gsoc2012.markdittmer.org
    --
    {-# LANGUAGE LambdaCase #-}
    {-# LANGUAGE MultiParamTypeClasses #-}
    {-# LANGUAGE NamedFieldPuns #-}
    {-# LANGUAGE ViewPatterns #-}

    {-# OPTIONS_GHC -fno-warn-orphans #-}

    module System.FSNotify.Linux (
      FileListener(..)
      , NativeManager
      ) where

    import Control.Concurrent.MVar
    import Control.Exception.Safe as E
    import Control.Monad
    import Data.Function
    import Data.Monoid
    import Data.String
    import Data.Time.Clock (UTCTime)
    import Data.Time.Clock.POSIX
    import Prelude hiding (FilePath)
    import System.FSNotify.Find
    import System.FSNotify.Linux.Util
    import System.FSNotify.Listener
    import System.FSNotify.Types
    import System.FilePath ((</>))
    import qualified System.INotify as INo
    import System.Posix.ByteString (RawFilePath)
    import System.Posix.Files (getFileStatus, isDirectory, modificationTimeHiRes)

    data INotifyListener = INotifyListener { INotifyListener -> INotify
    listenerINotify :: INo.INotify }

    type NativeManager = INotifyListener

    data EventVarietyMismatchException = EventVarietyMismatchException deriving (Int -> EventVarietyMismatchException -> ShowS
    [EventVarietyMismatchException] -> ShowS
    EventVarietyMismatchException -> String
    (Int -> EventVarietyMismatchException -> ShowS)
    -> (EventVarietyMismatchException -> String)
    -> ([EventVarietyMismatchException] -> ShowS)
    -> Show EventVarietyMismatchException
    forall a.
    (Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
    $cshowsPrec :: Int -> EventVarietyMismatchException -> ShowS
    showsPrec :: Int -> EventVarietyMismatchException -> ShowS
    $cshow :: EventVarietyMismatchException -> String
    show :: EventVarietyMismatchException -> String
    $cshowList :: [EventVarietyMismatchException] -> ShowS
    showList :: [EventVarietyMismatchException] -> ShowS
    Show, Typeable)
    instance Exception EventVarietyMismatchException

    fsnEvents :: RawFilePath -> UTCTime -> INo.Event -> IO [Event]
    fsnEvents :: RawFilePath -> UTCTime -> Event -> IO [Event]
    fsnEvents RawFilePath
    basePath' UTCTime
    timestamp (INo.Attributes (Bool -> EventIsDirectory
    boolToIsDirectory -> EventIsDirectory
    isDir) (Just RawFilePath
    raw)) = do
      basePath <- RawFilePath -> IO String
    fromRawFilePath RawFilePath
    basePath'
      fromHinotifyPath raw >>= \String
    name -> [Event] -> IO [Event]
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return [String -> UTCTime -> EventIsDirectory -> Event
    ModifiedAttributes (String
    basePath String -> ShowS
    </> String
    name) UTCTime
    timestamp EventIsDirectory
    isDir]
    fsnEvents RawFilePath
    basePath' UTCTime
    timestamp (INo.Modified (Bool -> EventIsDirectory
    boolToIsDirectory -> EventIsDirectory
    isDir) (Just RawFilePath
    raw)) = do
      basePath <- RawFilePath -> IO String
    fromRawFilePath RawFilePath
    basePath'
      fromHinotifyPath raw >>= \String
    name -> [Event] -> IO [Event]
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return [String -> UTCTime -> EventIsDirectory -> Event
    Modified (String
    basePath String -> ShowS
    </> String
    name) UTCTime
    timestamp EventIsDirectory
    isDir]
    fsnEvents RawFilePath
    basePath' UTCTime
    timestamp (INo.Closed (Bool -> EventIsDirectory
    boolToIsDirectory -> EventIsDirectory
    isDir) (Just RawFilePath
    raw) Bool
    True) = do
      basePath <- RawFilePath -> IO String
    fromRawFilePath RawFilePath
    basePath'
      fromHinotifyPath raw >>= \String
    name -> [Event] -> IO [Event]
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return [String -> UTCTime -> EventIsDirectory -> Event
    CloseWrite (String
    basePath String -> ShowS
    </> String
    name) UTCTime
    timestamp EventIsDirectory
    isDir]
    fsnEvents RawFilePath
    basePath' UTCTime
    timestamp (INo.Created (Bool -> EventIsDirectory
    boolToIsDirectory -> EventIsDirectory
    isDir) RawFilePath
    raw) = do
      basePath <- RawFilePath -> IO String
    fromRawFilePath RawFilePath
    basePath'
      fromHinotifyPath raw >>= \String
    name -> [Event] -> IO [Event]
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return [String -> UTCTime -> EventIsDirectory -> Event
    Added (String
    basePath String -> ShowS
    </> String
    name) UTCTime
    timestamp EventIsDirectory
    isDir]
    fsnEvents RawFilePath
    basePath' UTCTime
    timestamp (INo.MovedOut (Bool -> EventIsDirectory
    boolToIsDirectory -> EventIsDirectory
    isDir) RawFilePath
    raw Cookie
    _cookie) = do
      basePath <- RawFilePath -> IO String
    fromRawFilePath RawFilePath
    basePath'
      fromHinotifyPath raw >>= \String
    name -> [Event] -> IO [Event]
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return [String -> UTCTime -> EventIsDirectory -> Event
    Removed (String
    basePath String -> ShowS
    </> String
    name) UTCTime
    timestamp EventIsDirectory
    isDir]
    fsnEvents RawFilePath
    basePath' UTCTime
    timestamp (INo.MovedIn (Bool -> EventIsDirectory
    boolToIsDirectory -> EventIsDirectory
    isDir) RawFilePath
    raw Cookie
    _cookie) = do
      basePath <- RawFilePath -> IO String
    fromRawFilePath RawFilePath
    basePath'
      fromHinotifyPath raw >>= \String
    name -> [Event] -> IO [Event]
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return [String -> UTCTime -> EventIsDirectory -> Event
    Added (String
    basePath String -> ShowS
    </> String
    name) UTCTime
    timestamp EventIsDirectory
    isDir]
    fsnEvents RawFilePath
    basePath' UTCTime
    timestamp (INo.Deleted (Bool -> EventIsDirectory
    boolToIsDirectory -> EventIsDirectory
    isDir) RawFilePath
    raw) = do
      basePath <- RawFilePath -> IO String
    fromRawFilePath RawFilePath
    basePath'
      fromHinotifyPath raw >>= \String
    name -> [Event] -> IO [Event]
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return [String -> UTCTime -> EventIsDirectory -> Event
    Removed (String
    basePath String -> ShowS
    </> String
    name) UTCTime
    timestamp EventIsDirectory
    isDir]
    fsnEvents RawFilePath
    basePath' UTCTime
    timestamp Event
    INo.DeletedSelf = do
      basePath <- RawFilePath -> IO String
    fromRawFilePath RawFilePath
    basePath'
      return [WatchedDirectoryRemoved basePath timestamp IsDirectory]
    fsnEvents RawFilePath
    _ UTCTime
    _ Event
    INo.Ignored = [Event] -> IO [Event]
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return []
    fsnEvents RawFilePath
    basePath' UTCTime
    timestamp Event
    inoEvent = do
      basePath <- RawFilePath -> IO String
    fromRawFilePath RawFilePath
    basePath'
      return [Unknown basePath timestamp IsFile (show inoEvent)]

    handleInoEvent :: ActionPredicate -> EventCallback -> RawFilePath -> MVar Bool -> INo.Event -> IO ()
    handleInoEvent :: ActionPredicate
    -> EventCallback -> RawFilePath -> MVar Bool -> Event -> IO ()
    handleInoEvent ActionPredicate
    actPred EventCallback
    callback RawFilePath
    basePath MVar Bool
    watchStillExistsVar Event
    inoEvent = do
      Bool -> IO () -> IO ()
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
    when (Event
    INo.DeletedSelf Event -> Event -> Bool
    forall a. Eq a => a -> a -> Bool
    == Event
    inoEvent) (IO () -> IO ()) -> IO () -> IO ()
    forall a b. (a -> b) -> a -> b
    $ MVar Bool -> (Bool -> IO Bool) -> IO ()
    forall a. MVar a -> (a -> IO a) -> IO ()
    modifyMVar_ MVar Bool
    watchStillExistsVar ((Bool -> IO Bool) -> IO ()) -> (Bool -> IO Bool) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ IO Bool -> Bool -> IO Bool
    forall a b. a -> b -> a
    const (IO Bool -> Bool -> IO Bool) -> IO Bool -> Bool -> IO Bool
    forall a b. (a -> b) -> a -> b
    $ Bool -> IO Bool
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Bool
    False

      currentTime <- IO UTCTime
    getCurrentTime
      events <- fsnEvents basePath currentTime inoEvent
      forM_ events $ \Event
    event -> Bool -> IO () -> IO ()
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
    when (ActionPredicate
    actPred Event
    event) (IO () -> IO ()) -> IO () -> IO ()
    forall a b. (a -> b) -> a -> b
    $ EventCallback
    callback Event
    event

    varieties :: [INo.EventVariety]
    varieties :: [EventVariety]
    varieties = [EventVariety
    INo.Create, EventVariety
    INo.Delete, EventVariety
    INo.MoveIn, EventVariety
    INo.MoveOut, EventVariety
    INo.Attrib, EventVariety
    INo.Modify, EventVariety
    INo.CloseWrite, EventVariety
    INo.DeleteSelf]

    instance FileListener INotifyListener () where
      initSession :: () -> IO (Either Text INotifyListener)
    initSession ()
    _ = (IOException -> IO (Either Text INotifyListener))
    -> IO (Either Text INotifyListener)
    -> IO (Either Text INotifyListener)
    forall (m :: * -> *) e a.
    (HasCallStack, MonadCatch m, Exception e) =>
    (e -> m a) -> m a -> m a
    E.handle (\(IOException
    e :: IOException) -> Either Text INotifyListener -> IO (Either Text INotifyListener)
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return (Either Text INotifyListener -> IO (Either Text INotifyListener))
    -> Either Text INotifyListener -> IO (Either Text INotifyListener)
    forall a b. (a -> b) -> a -> b
    $ Text -> Either Text INotifyListener
    forall a b. a -> Either a b
    Left (Text -> Either Text INotifyListener)
    -> Text -> Either Text INotifyListener
    forall a b. (a -> b) -> a -> b
    $ String -> Text
    forall a. IsString a => String -> a
    fromString (String -> Text) -> String -> Text
    forall a b. (a -> b) -> a -> b
    $ IOException -> String
    forall a. Show a => a -> String
    show IOException
    e) (IO (Either Text INotifyListener)
     -> IO (Either Text INotifyListener))
    -> IO (Either Text INotifyListener)
    -> IO (Either Text INotifyListener)
    forall a b. (a -> b) -> a -> b
    $ do
        inotify <- IO INotify
    INo.initINotify
        return $ Right $ INotifyListener inotify

      killSession :: INotifyListener -> IO ()
    killSession (INotifyListener {INotify
    listenerINotify :: INotifyListener -> INotify
    listenerINotify :: INotify
    listenerINotify}) = INotify -> IO ()
    INo.killINotify INotify
    listenerINotify

      listen :: ListenFn INotifyListener ()
    listen WatchConfig
    _conf (INotifyListener {INotify
    listenerINotify :: INotifyListener -> INotify
    listenerINotify :: INotify
    listenerINotify}) String
    path ActionPredicate
    actPred EventCallback
    callback = do
        rawPath <- String -> IO RawFilePath
    toRawFilePath String
    path
        canonicalRawPath <- canonicalizeRawDirPath rawPath
        watchStillExistsVar <- newMVar True
        hinotifyPath <- rawToHinotifyPath canonicalRawPath
        wd <- INo.addWatch listenerINotify varieties hinotifyPath (handleInoEvent actPred callback canonicalRawPath watchStillExistsVar)
        return $
          modifyMVar_ watchStillExistsVar $ \Bool
    wse -> do
            Bool -> IO () -> IO ()
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
    when Bool
    wse (IO () -> IO ()) -> IO () -> IO ()
    forall a b. (a -> b) -> a -> b
    $ WatchDescriptor -> IO ()
    INo.removeWatch WatchDescriptor
    wd
            Bool -> IO Bool
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Bool
    False

      listenRecursive :: ListenFn INotifyListener ()
    listenRecursive WatchConfig
    _conf INotifyListener
    listener String
    initialPath ActionPredicate
    actPred EventCallback
    callback = do
        -- wdVar stores the list of created watch descriptors. We use it to
        -- cancel the whole recursive listening task.
        --
        -- To avoid a race condition (when a new watch is added right after
        -- we've stopped listening), we replace the MVar contents with Nothing
        -- to signify that the listening task is cancelled, and no new watches
        -- should be added.
        wdVar <- Maybe [(WatchDescriptor, MVar Bool)]
    -> IO (MVar (Maybe [(WatchDescriptor, MVar Bool)]))
    forall a. a -> IO (MVar a)
    newMVar ([(WatchDescriptor, MVar Bool)]
    -> Maybe [(WatchDescriptor, MVar Bool)]
    forall a. a -> Maybe a
    Just [])

        let
          removeWatches t (WatchDescriptor, MVar Bool)
    wds = t (WatchDescriptor, MVar Bool)
    -> ((WatchDescriptor, MVar Bool) -> IO ()) -> IO ()
    forall (t :: * -> *) (m :: * -> *) a b.
    (Foldable t, Monad m) =>
    t a -> (a -> m b) -> m ()
    forM_ t (WatchDescriptor, MVar Bool)
    wds (((WatchDescriptor, MVar Bool) -> IO ()) -> IO ())
    -> ((WatchDescriptor, MVar Bool) -> IO ()) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ \(WatchDescriptor
    wd, MVar Bool
    watchStillExistsVar) ->
            MVar Bool -> (Bool -> IO Bool) -> IO ()
    forall a. MVar a -> (a -> IO a) -> IO ()
    modifyMVar_ MVar Bool
    watchStillExistsVar ((Bool -> IO Bool) -> IO ()) -> (Bool -> IO Bool) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ \Bool
    wse -> do
              Bool -> IO () -> IO ()
    forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
    when Bool
    wse (IO () -> IO ()) -> IO () -> IO ()
    forall a b. (a -> b) -> a -> b
    $
                (SomeException -> IO ()) -> IO () -> IO ()
    forall (m :: * -> *) e a.
    (HasCallStack, MonadCatch m, Exception e) =>
    (e -> m a) -> m a -> m a
    handle (\(SomeException
    e :: SomeException) -> String -> IO ()
    putStrLn (String
    "Error removing watch: " String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> WatchDescriptor -> String
    forall a. Show a => a -> String
    show WatchDescriptor
    wd String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> String
    " (" String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> SomeException -> String
    forall a. Show a => a -> String
    show SomeException
    e String -> ShowS
    forall a. Semigroup a => a -> a -> a
    <> String
    ")"))
                       (WatchDescriptor -> IO ()
    INo.removeWatch WatchDescriptor
    wd)
              Bool -> IO Bool
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Bool
    False

          stopListening = MVar (Maybe [(WatchDescriptor, MVar Bool)])
    -> (Maybe [(WatchDescriptor, MVar Bool)]
        -> IO (Maybe [(WatchDescriptor, MVar Bool)]))
    -> IO ()
    forall a. MVar a -> (a -> IO a) -> IO ()
    modifyMVar_ MVar (Maybe [(WatchDescriptor, MVar Bool)])
    wdVar ((Maybe [(WatchDescriptor, MVar Bool)]
      -> IO (Maybe [(WatchDescriptor, MVar Bool)]))
     -> IO ())
    -> (Maybe [(WatchDescriptor, MVar Bool)]
        -> IO (Maybe [(WatchDescriptor, MVar Bool)]))
    -> IO ()
    forall a b. (a -> b) -> a -> b
    $ \Maybe [(WatchDescriptor, MVar Bool)]
    x -> IO ()
    -> ([(WatchDescriptor, MVar Bool)] -> IO ())
    -> Maybe [(WatchDescriptor, MVar Bool)]
    -> IO ()
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe (() -> IO ()
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()) [(WatchDescriptor, MVar Bool)] -> IO ()
    forall {t :: * -> *}.
    Foldable t =>
    t (WatchDescriptor, MVar Bool) -> IO ()
    removeWatches Maybe [(WatchDescriptor, MVar Bool)]
    x IO ()
    -> IO (Maybe [(WatchDescriptor, MVar Bool)])
    -> IO (Maybe [(WatchDescriptor, MVar Bool)])
    forall a b. IO a -> IO b -> IO b
    forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
    >> Maybe [(WatchDescriptor, MVar Bool)]
    -> IO (Maybe [(WatchDescriptor, MVar Bool)])
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Maybe [(WatchDescriptor, MVar Bool)]
    forall a. Maybe a
    Nothing

        -- Add watches to this directory plus every sub-directory
        rawInitialPath <- toRawFilePath initialPath
        rawCanonicalInitialPath <- canonicalizeRawDirPath rawInitialPath
        watchDirectoryRecursively listener wdVar actPred callback True rawCanonicalInitialPath
        traverseAllDirs rawCanonicalInitialPath $ \RawFilePath
    subPath ->
          INotifyListener
    -> MVar (Maybe [(WatchDescriptor, MVar Bool)])
    -> ActionPredicate
    -> EventCallback
    -> Bool
    -> RawFilePath
    -> IO ()
    watchDirectoryRecursively INotifyListener
    listener MVar (Maybe [(WatchDescriptor, MVar Bool)])
    wdVar ActionPredicate
    actPred EventCallback
    callback Bool
    False RawFilePath
    subPath

        return stopListening

    type RecursiveWatches = MVar (Maybe [(INo.WatchDescriptor, MVar Bool)])

    watchDirectoryRecursively :: INotifyListener -> RecursiveWatches -> ActionPredicate -> EventCallback -> Bool -> RawFilePath -> IO ()
    watchDirectoryRecursively :: INotifyListener
    -> MVar (Maybe [(WatchDescriptor, MVar Bool)])
    -> ActionPredicate
    -> EventCallback
    -> Bool
    -> RawFilePath
    -> IO ()
    watchDirectoryRecursively listener :: INotifyListener
    listener@(INotifyListener {INotify
    listenerINotify :: INotifyListener -> INotify
    listenerINotify :: INotify
    listenerINotify}) MVar (Maybe [(WatchDescriptor, MVar Bool)])
    wdVar ActionPredicate
    actPred EventCallback
    callback Bool
    isRootWatchedDir RawFilePath
    rawFilePath = do
      MVar (Maybe [(WatchDescriptor, MVar Bool)])
    -> (Maybe [(WatchDescriptor, MVar Bool)]
        -> IO (Maybe [(WatchDescriptor, MVar Bool)]))
    -> IO ()
    forall a. MVar a -> (a -> IO a) -> IO ()
    modifyMVar_ MVar (Maybe [(WatchDescriptor, MVar Bool)])
    wdVar ((Maybe [(WatchDescriptor, MVar Bool)]
      -> IO (Maybe [(WatchDescriptor, MVar Bool)]))
     -> IO ())
    -> (Maybe [(WatchDescriptor, MVar Bool)]
        -> IO (Maybe [(WatchDescriptor, MVar Bool)]))
    -> IO ()
    forall a b. (a -> b) -> a -> b
    $ \case
        Maybe [(WatchDescriptor, MVar Bool)]
    Nothing -> Maybe [(WatchDescriptor, MVar Bool)]
    -> IO (Maybe [(WatchDescriptor, MVar Bool)])
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Maybe [(WatchDescriptor, MVar Bool)]
    forall a. Maybe a
    Nothing
        Just [(WatchDescriptor, MVar Bool)]
    wds -> do
          watchStillExistsVar <- Bool -> IO (MVar Bool)
    forall a. a -> IO (MVar a)
    newMVar Bool
    True
          hinotifyPath <- rawToHinotifyPath rawFilePath
          wd <- INo.addWatch listenerINotify varieties hinotifyPath (handleRecursiveEvent rawFilePath actPred callback watchStillExistsVar isRootWatchedDir listener wdVar)
          return $ Just ((wd, watchStillExistsVar):wds)

    handleRecursiveEvent :: RawFilePath -> ActionPredicate -> EventCallback -> MVar Bool -> Bool -> INotifyListener -> RecursiveWatches -> INo.Event -> IO ()
    handleRecursiveEvent :: RawFilePath
    -> ActionPredicate
    -> EventCallback
    -> MVar Bool
    -> Bool
    -> INotifyListener
    -> MVar (Maybe [(WatchDescriptor, MVar Bool)])
    -> Event
    -> IO ()
    handleRecursiveEvent RawFilePath
    baseDir ActionPredicate
    actPred EventCallback
    callback MVar Bool
    watchStillExistsVar Bool
    isRootWatchedDir INotifyListener
    listener MVar (Maybe [(WatchDescriptor, MVar Bool)])
    wdVar Event
    event = do
      case Event
    event of
        (INo.Created Bool
    True RawFilePath
    hiNotifyPath) -> do
          -- A new directory was created, so add recursive inotify watches to it
          rawDirPath <- RawFilePath -> IO RawFilePath
    rawFromHinotifyPath RawFilePath
    hiNotifyPath
          let newRawDir = RawFilePath
    baseDir RawFilePath -> RawFilePath -> RawFilePath
    <//> RawFilePath
    rawDirPath
          timestampBeforeAddingWatch <- getPOSIXTime
          watchDirectoryRecursively listener wdVar actPred callback False newRawDir

          newDir <- fromRawFilePath newRawDir

          -- Find all files/folders that might have been created *after* the timestamp, and hence might have been
          -- missed by the watch
          -- TODO: there's a chance of this generating double events, fix
          files <- find False newDir -- TODO: expose the ability to set followSymlinks to True?
          forM_ files $ \String
    newPath -> do
            fileStatus <- String -> IO FileStatus
    getFileStatus String
    newPath
            let modTime = FileStatus -> POSIXTime
    modificationTimeHiRes FileStatus
    fileStatus
            when (modTime > timestampBeforeAddingWatch) $ do
              let isDir = if FileStatus -> Bool
    isDirectory FileStatus
    fileStatus then EventIsDirectory
    IsDirectory else EventIsDirectory
    IsFile
              let addedEvent = (String -> UTCTime -> EventIsDirectory -> Event
    Added (String
    newDir String -> ShowS
    </> String
    newPath) (POSIXTime -> UTCTime
    posixSecondsToUTCTime POSIXTime
    timestampBeforeAddingWatch) EventIsDirectory
    isDir)
              when (actPred addedEvent) $ callback addedEvent

        Event
    _ -> () -> IO ()
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()

      -- If the watched directory was removed, mark the watch as already removed
      case Event
    event of
        Event
    INo.DeletedSelf -> MVar Bool -> (Bool -> IO Bool) -> IO ()
    forall a. MVar a -> (a -> IO a) -> IO ()
    modifyMVar_ MVar Bool
    watchStillExistsVar ((Bool -> IO Bool) -> IO ()) -> (Bool -> IO Bool) -> IO ()
    forall a b. (a -> b) -> a -> b
    $ IO Bool -> Bool -> IO Bool
    forall a b. a -> b -> a
    const (IO Bool -> Bool -> IO Bool) -> IO Bool -> Bool -> IO Bool
    forall a b. (a -> b) -> a -> b
    $ Bool -> IO Bool
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return Bool
    False
        Event
    _ -> () -> IO ()
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()

      -- Forward the event. Ignore a DeletedSelf if we're not on the root directory,
      -- since the watch above us will pick up the delete of that directory.
      case Event
    event of
        Event
    INo.DeletedSelf | Bool -> Bool
    not Bool
    isRootWatchedDir -> () -> IO ()
    forall a. a -> IO a
    forall (m :: * -> *) a. Monad m => a -> m a
    return ()
        Event
    _ -> ActionPredicate
    -> EventCallback -> RawFilePath -> MVar Bool -> Event -> IO ()
    handleInoEvent ActionPredicate
    actPred EventCallback
    callback RawFilePath
    baseDir MVar Bool
    watchStillExistsVar Event
    event