content: {-# LANGUAGE OverloadedStrings #-}
  -----------------------------------------------------------------------------
  -- |
  -- Module      : System.Taffybar.Widget.SNITray
  -- Copyright   : (c) Ivan A. Malison
  -- License     : BSD3-style (see LICENSE)
  --
  -- Maintainer  : Ivan A. Malison
  -- Stability   : unstable
  -- Portability : unportable
  --
  -- This module exports functions for the construction of
  -- StatusNotifierItem/AppIndicator tray widgets, supplied by the
  -- "StatusNotifier.Tray" module from the gtk-sni-tray library. These widgets do
  -- not support the older XEMBED protocol, although bridges like
  -- xembed-sni-proxy do allow sni trays to provide limited support for XEMBED
  -- tray icons.
  --
  -- Unless 'sniTrayThatStartsWatcherEvenThoughThisIsABadWayToDoIt' is used it is
  -- necessary to run status-notifier-watcher from the
  -- [status-notifier-item](https://github.com/taffybar/status-notifier-item)
  -- package before starting taffybar when using the functions defined in this
  -- module. Using 'sniTrayThatStartsWatcherEvenThoughThisIsABadWayToDoIt' is
  -- generally not recommended, because it can lead to issues with the
  -- registration of tray icons if taffybar crashes/restarts, or if tray icon
  -- providing applications are ever started before taffybar.
  -----------------------------------------------------------------------------
  
  module System.Taffybar.Widget.SNITray
    ( TrayParams
    , module System.Taffybar.Widget.SNITray
    ) where
  
  import           Control.Monad.Trans.Class
  import           Control.Monad.Trans.Reader
  import qualified GI.Gtk
  import qualified StatusNotifier.Host.Service as H
  import           StatusNotifier.Tray
  import           System.Posix.Process
  import           System.Taffybar.Context
  import           System.Taffybar.Widget.Util
  import           Text.Printf
  
  -- | Build a new StatusNotifierItem tray that will share a host with any other
  -- trays that are constructed automatically
  sniTrayNew :: TaffyIO GI.Gtk.Widget
  sniTrayNew = sniTrayNewFromParams defaultTrayParams
  
  -- | Build a new StatusNotifierItem tray from the provided 'TrayParams'.
  sniTrayNewFromParams :: TrayParams -> TaffyIO GI.Gtk.Widget
  sniTrayNewFromParams params =
    getTrayHost False >>= sniTrayNewFromHostParams params
  
  -- | Build a new StatusNotifierItem tray from the provided 'TrayParams' and
  -- 'H.Host'.
  sniTrayNewFromHostParams :: TrayParams -> H.Host -> TaffyIO GI.Gtk.Widget
  sniTrayNewFromHostParams params host = do
    client <- asks sessionDBusClient
    lift $ do
      tray <- buildTray host client params
      _ <- widgetSetClassGI tray "sni-tray"
      GI.Gtk.widgetShowAll tray
      GI.Gtk.toWidget tray
  
  -- | Build a new StatusNotifierItem tray that also starts its own watcher,
  -- without depending on status-notifier-icon. This will not register applets
  -- started before the watcher is started.
  sniTrayThatStartsWatcherEvenThoughThisIsABadWayToDoIt :: TaffyIO GI.Gtk.Widget
  sniTrayThatStartsWatcherEvenThoughThisIsABadWayToDoIt =
    getTrayHost True >>= sniTrayNewFromHostParams defaultTrayParams
  
  -- | Get a 'H.Host' from 'TaffyIO' internal state, that can be used to construct
  -- SNI tray widgets. The boolean parameter determines whether or not a watcher
  -- will be started the first time 'getTrayHost' is invoked.
  getTrayHost :: Bool -> TaffyIO H.Host
  getTrayHost startWatcher = getStateDefault $ do
    pid <- lift getProcessID
    client <- asks sessionDBusClient
    Just host <- lift $ H.build H.defaultParams
       { H.dbusClient = Just client
       , H.uniqueIdentifier = printf "taffybar-%s" $ show pid
       , H.startWatcher = startWatcher
       }
    return host
  
  