{-# LANGUAGE OverloadedStrings #-}
module System.Taffybar.Information.Chrome where

import           BroadcastChan
import           Control.Concurrent
import           Control.Monad
import           Control.Monad.Trans.Class
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
import qualified Data.Map as M
import           Data.Maybe
import qualified GI.GLib as Gdk
import qualified GI.GdkPixbuf as Gdk
import           Prelude
import           System.Log.Logger
import           System.Taffybar.Context
import           System.Taffybar.Information.EWMHDesktopInfo
import           System.Taffybar.Information.SafeX11
import           Text.Read hiding (lift)
import           Text.Regex
import           Web.Scotty

logIO :: System.Log.Logger.Priority -> String -> IO ()
logIO :: Priority -> String -> IO ()
logIO = String -> Priority -> String -> IO ()
logM String
"System.Taffybar.Information.Chrome"

data ChromeTabImageData = ChromeTabImageData
  { ChromeTabImageData -> Pixbuf
tabImageData :: Gdk.Pixbuf
  , ChromeTabImageData -> Int
tabImageDataId :: Int
  }

newtype ChromeTabImageDataState =
  ChromeTabImageDataState
  (MVar (M.Map Int ChromeTabImageData), BroadcastChan Out ChromeTabImageData)

getChromeTabImageDataState :: TaffyIO ChromeTabImageDataState
getChromeTabImageDataState :: TaffyIO ChromeTabImageDataState
getChromeTabImageDataState = do
  ChromeFaviconServerPort Int
port <- ChromeFaviconServerPort
-> Maybe ChromeFaviconServerPort -> ChromeFaviconServerPort
forall a. a -> Maybe a -> a
fromMaybe (Int -> ChromeFaviconServerPort
ChromeFaviconServerPort Int
5000) (Maybe ChromeFaviconServerPort -> ChromeFaviconServerPort)
-> ReaderT Context IO (Maybe ChromeFaviconServerPort)
-> ReaderT Context IO ChromeFaviconServerPort
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ReaderT Context IO (Maybe ChromeFaviconServerPort)
forall t. Typeable t => Taffy IO (Maybe t)
getState
  TaffyIO ChromeTabImageDataState -> TaffyIO ChromeTabImageDataState
forall t. Typeable t => Taffy IO t -> Taffy IO t
getStateDefault (Int -> TaffyIO ChromeTabImageDataState
listenForChromeFaviconUpdates Int
port)

getChromeTabImageDataChannel :: TaffyIO (BroadcastChan Out ChromeTabImageData)
getChromeTabImageDataChannel :: TaffyIO (BroadcastChan Out ChromeTabImageData)
getChromeTabImageDataChannel = do
  ChromeTabImageDataState (MVar (Map Int ChromeTabImageData)
_, BroadcastChan Out ChromeTabImageData
chan) <- TaffyIO ChromeTabImageDataState
getChromeTabImageDataState
  BroadcastChan Out ChromeTabImageData
-> TaffyIO (BroadcastChan Out ChromeTabImageData)
forall a. a -> ReaderT Context IO a
forall (m :: * -> *) a. Monad m => a -> m a
return BroadcastChan Out ChromeTabImageData
chan

getChromeTabImageDataTable :: TaffyIO (MVar (M.Map Int ChromeTabImageData))
getChromeTabImageDataTable :: TaffyIO (MVar (Map Int ChromeTabImageData))
getChromeTabImageDataTable = do
  ChromeTabImageDataState (MVar (Map Int ChromeTabImageData)
table, BroadcastChan Out ChromeTabImageData
_) <- TaffyIO ChromeTabImageDataState
getChromeTabImageDataState
  MVar (Map Int ChromeTabImageData)
-> TaffyIO (MVar (Map Int ChromeTabImageData))
forall a. a -> ReaderT Context IO a
forall (m :: * -> *) a. Monad m => a -> m a
return MVar (Map Int ChromeTabImageData)
table

newtype ChromeFaviconServerPort = ChromeFaviconServerPort Int

listenForChromeFaviconUpdates :: Int -> TaffyIO ChromeTabImageDataState
listenForChromeFaviconUpdates :: Int -> TaffyIO ChromeTabImageDataState
listenForChromeFaviconUpdates Int
port = do
  MVar (Map Int ChromeTabImageData)
infoVar <- IO (MVar (Map Int ChromeTabImageData))
-> TaffyIO (MVar (Map Int ChromeTabImageData))
forall (m :: * -> *) a. Monad m => m a -> ReaderT Context m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (IO (MVar (Map Int ChromeTabImageData))
 -> TaffyIO (MVar (Map Int ChromeTabImageData)))
-> IO (MVar (Map Int ChromeTabImageData))
-> TaffyIO (MVar (Map Int ChromeTabImageData))
forall a b. (a -> b) -> a -> b
$ Map Int ChromeTabImageData
-> IO (MVar (Map Int ChromeTabImageData))
forall a. a -> IO (MVar a)
newMVar Map Int ChromeTabImageData
forall k a. Map k a
M.empty
  BroadcastChan In ChromeTabImageData
inChan <- ReaderT Context IO (BroadcastChan In ChromeTabImageData)
forall (m :: * -> *) a. MonadIO m => m (BroadcastChan In a)
newBroadcastChan
  BroadcastChan Out ChromeTabImageData
outChan <- BroadcastChan In ChromeTabImageData
-> TaffyIO (BroadcastChan Out ChromeTabImageData)
forall (m :: * -> *) (dir :: Direction) a.
MonadIO m =>
BroadcastChan dir a -> m (BroadcastChan Out a)
newBChanListener BroadcastChan In ChromeTabImageData
inChan
  ThreadId
_ <- IO ThreadId -> ReaderT Context IO ThreadId
forall (m :: * -> *) a. Monad m => m a -> ReaderT Context m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (IO ThreadId -> ReaderT Context IO ThreadId)
-> IO ThreadId -> ReaderT Context IO ThreadId
forall a b. (a -> b) -> a -> b
$ IO () -> IO ThreadId
forkIO (IO () -> IO ThreadId) -> IO () -> IO ThreadId
forall a b. (a -> b) -> a -> b
$ Int -> ScottyM () -> IO ()
scotty Int
port (ScottyM () -> IO ()) -> ScottyM () -> IO ()
forall a b. (a -> b) -> a -> b
$
    RoutePattern -> ActionM () -> ScottyM ()
post RoutePattern
"/setTabImageData/:tabID" (ActionM () -> ScottyM ()) -> ActionM () -> ScottyM ()
forall a b. (a -> b) -> a -> b
$ do
      Int
tabID <- Text -> ActionM Int
forall a. Parsable a => Text -> ActionM a
param Text
"tabID"
      ByteString
imageData <- ByteString -> ByteString
LBS.toStrict (ByteString -> ByteString)
-> ActionT Text IO ByteString -> ActionT Text IO ByteString
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ActionT Text IO ByteString
body
      Bool -> ActionM () -> ActionM ()
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (ByteString -> Int
BS.length ByteString
imageData Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0) (ActionM () -> ActionM ()) -> ActionM () -> ActionM ()
forall a b. (a -> b) -> a -> b
$ IO () -> ActionM ()
forall (m :: * -> *) a. Monad m => m a -> ActionT Text m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (IO () -> ActionM ()) -> IO () -> ActionM ()
forall a b. (a -> b) -> a -> b
$ do
        PixbufLoader
loader <- IO PixbufLoader
forall (m :: * -> *). (HasCallStack, MonadIO m) => m PixbufLoader
Gdk.pixbufLoaderNew
        PixbufLoader -> Bytes -> IO ()
forall (m :: * -> *) a.
(HasCallStack, MonadIO m, IsPixbufLoader a) =>
a -> Bytes -> m ()
Gdk.pixbufLoaderWriteBytes PixbufLoader
loader (Bytes -> IO ()) -> IO Bytes -> IO ()
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< Maybe ByteString -> IO Bytes
forall (m :: * -> *).
(HasCallStack, MonadIO m) =>
Maybe ByteString -> m Bytes
Gdk.bytesNew (ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just ByteString
imageData)
        PixbufLoader -> IO ()
forall (m :: * -> *) a.
(HasCallStack, MonadIO m, IsPixbufLoader a) =>
a -> m ()
Gdk.pixbufLoaderClose PixbufLoader
loader
        let updateChannelAndMVar :: Pixbuf -> IO ()
updateChannelAndMVar Pixbuf
pixbuf =
              let chromeTabImageData :: ChromeTabImageData
chromeTabImageData =
                    ChromeTabImageData
                    { tabImageData :: Pixbuf
tabImageData = Pixbuf
pixbuf
                    , tabImageDataId :: Int
tabImageDataId = Int
tabID
                    }
              in
                MVar (Map Int ChromeTabImageData)
-> (Map Int ChromeTabImageData -> IO (Map Int ChromeTabImageData))
-> IO ()
forall a. MVar a -> (a -> IO a) -> IO ()
modifyMVar_ MVar (Map Int ChromeTabImageData)
infoVar ((Map Int ChromeTabImageData -> IO (Map Int ChromeTabImageData))
 -> IO ())
-> (Map Int ChromeTabImageData -> IO (Map Int ChromeTabImageData))
-> IO ()
forall a b. (a -> b) -> a -> b
$ \Map Int ChromeTabImageData
currentMap ->
                  do
                    Bool
_ <- BroadcastChan In ChromeTabImageData
-> ChromeTabImageData -> IO Bool
forall (m :: * -> *) a.
MonadIO m =>
BroadcastChan In a -> a -> m Bool
writeBChan BroadcastChan In ChromeTabImageData
inChan ChromeTabImageData
chromeTabImageData
                    Map Int ChromeTabImageData -> IO (Map Int ChromeTabImageData)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Map Int ChromeTabImageData -> IO (Map Int ChromeTabImageData))
-> Map Int ChromeTabImageData -> IO (Map Int ChromeTabImageData)
forall a b. (a -> b) -> a -> b
$ Int
-> ChromeTabImageData
-> Map Int ChromeTabImageData
-> Map Int ChromeTabImageData
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert Int
tabID ChromeTabImageData
chromeTabImageData Map Int ChromeTabImageData
currentMap
        PixbufLoader -> IO (Maybe Pixbuf)
forall (m :: * -> *) a.
(HasCallStack, MonadIO m, IsPixbufLoader a) =>
a -> m (Maybe Pixbuf)
Gdk.pixbufLoaderGetPixbuf PixbufLoader
loader IO (Maybe Pixbuf) -> (Maybe Pixbuf -> IO ()) -> IO ()
forall a b. IO a -> (a -> IO b) -> IO b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= IO () -> (Pixbuf -> IO ()) -> Maybe Pixbuf -> 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 ()) Pixbuf -> IO ()
updateChannelAndMVar
  ChromeTabImageDataState -> TaffyIO ChromeTabImageDataState
forall a. a -> ReaderT Context IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (ChromeTabImageDataState -> TaffyIO ChromeTabImageDataState)
-> ChromeTabImageDataState -> TaffyIO ChromeTabImageDataState
forall a b. (a -> b) -> a -> b
$ (MVar (Map Int ChromeTabImageData),
 BroadcastChan Out ChromeTabImageData)
-> ChromeTabImageDataState
ChromeTabImageDataState (MVar (Map Int ChromeTabImageData)
infoVar, BroadcastChan Out ChromeTabImageData
outChan)

newtype X11WindowToChromeTabId = X11WindowToChromeTabId (MVar (M.Map X11Window Int))

getX11WindowToChromeTabId :: TaffyIO X11WindowToChromeTabId
getX11WindowToChromeTabId :: TaffyIO X11WindowToChromeTabId
getX11WindowToChromeTabId =
  TaffyIO X11WindowToChromeTabId -> TaffyIO X11WindowToChromeTabId
forall t. Typeable t => Taffy IO t -> Taffy IO t
getStateDefault (TaffyIO X11WindowToChromeTabId -> TaffyIO X11WindowToChromeTabId)
-> TaffyIO X11WindowToChromeTabId -> TaffyIO X11WindowToChromeTabId
forall a b. (a -> b) -> a -> b
$ MVar (Map X11Window Int) -> X11WindowToChromeTabId
X11WindowToChromeTabId (MVar (Map X11Window Int) -> X11WindowToChromeTabId)
-> ReaderT Context IO (MVar (Map X11Window Int))
-> TaffyIO X11WindowToChromeTabId
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ReaderT Context IO (MVar (Map X11Window Int))
maintainX11WindowToChromeTabId

maintainX11WindowToChromeTabId :: TaffyIO (MVar (M.Map X11Window Int))
maintainX11WindowToChromeTabId :: ReaderT Context IO (MVar (Map X11Window Int))
maintainX11WindowToChromeTabId = do
  Map X11Window Int
startTabMap <- Map X11Window Int -> TaffyIO (Map X11Window Int)
updateTabMap Map X11Window Int
forall k a. Map k a
M.empty
  MVar (Map X11Window Int)
tabMapVar <- IO (MVar (Map X11Window Int))
-> ReaderT Context IO (MVar (Map X11Window Int))
forall (m :: * -> *) a. Monad m => m a -> ReaderT Context m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (IO (MVar (Map X11Window Int))
 -> ReaderT Context IO (MVar (Map X11Window Int)))
-> IO (MVar (Map X11Window Int))
-> ReaderT Context IO (MVar (Map X11Window Int))
forall a b. (a -> b) -> a -> b
$ Map X11Window Int -> IO (MVar (Map X11Window Int))
forall a. a -> IO (MVar a)
newMVar Map X11Window Int
startTabMap
  let handleEvent :: Event -> ReaderT Context IO ()
handleEvent PropertyEvent { ev_window :: Event -> X11Window
ev_window = X11Window
window } =
        do
          String
title <- String -> X11Property String -> TaffyIO String
forall a. a -> X11Property a -> TaffyIO a
runX11Def String
"" (X11Property String -> TaffyIO String)
-> X11Property String -> TaffyIO String
forall a b. (a -> b) -> a -> b
$ X11Window -> X11Property String
getWindowTitle X11Window
window
          IO () -> ReaderT Context IO ()
forall (m :: * -> *) a. Monad m => m a -> ReaderT Context m a
forall (t :: (* -> *) -> * -> *) (m :: * -> *) a.
(MonadTrans t, Monad m) =>
m a -> t m a
lift (IO () -> ReaderT Context IO ()) -> IO () -> ReaderT Context IO ()
forall a b. (a -> b) -> a -> b
$ MVar (Map X11Window Int)
-> (Map X11Window Int -> IO (Map X11Window Int)) -> IO ()
forall a. MVar a -> (a -> IO a) -> IO ()
modifyMVar_ MVar (Map X11Window Int)
tabMapVar ((Map X11Window Int -> IO (Map X11Window Int)) -> IO ())
-> (Map X11Window Int -> IO (Map X11Window Int)) -> IO ()
forall a b. (a -> b) -> a -> b
$ \Map X11Window Int
currentMap -> do
            let newMap :: Map X11Window Int
newMap = Map X11Window Int -> (X11Window, String) -> Map X11Window Int
addTabIdEntry Map X11Window Int
currentMap (X11Window
window, String
title)
            Priority -> String -> IO ()
logIO Priority
DEBUG (Map X11Window Int -> String
forall a. Show a => a -> String
show Map X11Window Int
newMap)
            Map X11Window Int -> IO (Map X11Window Int)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Map X11Window Int
newMap
      handleEvent Event
_ = () -> ReaderT Context IO ()
forall a. a -> ReaderT Context IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
  Unique
_ <- [String] -> (Event -> ReaderT Context IO ()) -> Taffy IO Unique
subscribeToPropertyEvents [String
ewmhWMName] Event -> ReaderT Context IO ()
handleEvent
  MVar (Map X11Window Int)
-> ReaderT Context IO (MVar (Map X11Window Int))
forall a. a -> ReaderT Context IO a
forall (m :: * -> *) a. Monad m => a -> m a
return MVar (Map X11Window Int)
tabMapVar

tabIDRegex :: Regex
tabIDRegex :: Regex
tabIDRegex = String -> Bool -> Bool -> Regex
mkRegexWithOpts String
"[|]%([0-9]*)%[|]" Bool
True Bool
True

getTabIdFromTitle :: String -> Maybe Int
getTabIdFromTitle :: String -> Maybe Int
getTabIdFromTitle String
title =
  Regex -> String -> Maybe [String]
matchRegex Regex
tabIDRegex String
title Maybe [String] -> ([String] -> Maybe String) -> Maybe String
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= [String] -> Maybe String
forall a. [a] -> Maybe a
listToMaybe Maybe String -> (String -> Maybe Int) -> Maybe Int
forall a b. Maybe a -> (a -> Maybe b) -> Maybe b
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= String -> Maybe Int
forall a. Read a => String -> Maybe a
readMaybe

addTabIdEntry :: M.Map X11Window Int -> (X11Window, String) -> M.Map X11Window Int
addTabIdEntry :: Map X11Window Int -> (X11Window, String) -> Map X11Window Int
addTabIdEntry Map X11Window Int
theMap (X11Window
win, String
title) =
          Map X11Window Int
-> (Int -> Map X11Window Int) -> Maybe Int -> Map X11Window Int
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Map X11Window Int
theMap (((Int -> Map X11Window Int -> Map X11Window Int)
-> Map X11Window Int -> Int -> Map X11Window Int
forall a b c. (a -> b -> c) -> b -> a -> c
flip ((Int -> Map X11Window Int -> Map X11Window Int)
 -> Map X11Window Int -> Int -> Map X11Window Int)
-> (Int -> Map X11Window Int -> Map X11Window Int)
-> Map X11Window Int
-> Int
-> Map X11Window Int
forall a b. (a -> b) -> a -> b
$ X11Window -> Int -> Map X11Window Int -> Map X11Window Int
forall k a. Ord k => k -> a -> Map k a -> Map k a
M.insert X11Window
win) Map X11Window Int
theMap) (Maybe Int -> Map X11Window Int) -> Maybe Int -> Map X11Window Int
forall a b. (a -> b) -> a -> b
$ String -> Maybe Int
getTabIdFromTitle String
title

updateTabMap :: M.Map X11Window Int -> TaffyIO (M.Map X11Window Int)
updateTabMap :: Map X11Window Int -> TaffyIO (Map X11Window Int)
updateTabMap Map X11Window Int
tabMap =
  Map X11Window Int
-> X11Property (Map X11Window Int) -> TaffyIO (Map X11Window Int)
forall a. a -> X11Property a -> TaffyIO a
runX11Def Map X11Window Int
tabMap (X11Property (Map X11Window Int) -> TaffyIO (Map X11Window Int))
-> X11Property (Map X11Window Int) -> TaffyIO (Map X11Window Int)
forall a b. (a -> b) -> a -> b
$ do
    [X11Window]
wins <- X11Property [X11Window]
getWindows
    [String]
titles <- (X11Window -> X11Property String)
-> [X11Window] -> ReaderT X11Context IO [String]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM X11Window -> X11Property String
getWindowTitle [X11Window]
wins
    let winsWithTitles :: [(X11Window, String)]
winsWithTitles = [X11Window] -> [String] -> [(X11Window, String)]
forall a b. [a] -> [b] -> [(a, b)]
zip [X11Window]
wins [String]
titles
    Map X11Window Int -> X11Property (Map X11Window Int)
forall a. a -> ReaderT X11Context IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Map X11Window Int -> X11Property (Map X11Window Int))
-> Map X11Window Int -> X11Property (Map X11Window Int)
forall a b. (a -> b) -> a -> b
$ (Map X11Window Int -> (X11Window, String) -> Map X11Window Int)
-> Map X11Window Int -> [(X11Window, String)] -> Map X11Window Int
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl Map X11Window Int -> (X11Window, String) -> Map X11Window Int
addTabIdEntry Map X11Window Int
tabMap [(X11Window, String)]
winsWithTitles