{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Patat.Images.WezTerm
( backend
) where
import Codec.Picture (DynamicImage,
Image (imageHeight, imageWidth),
decodeImage, dynamicMap)
import Control.Exception (throwIO)
import Control.Monad (unless, when)
import qualified Data.Aeson as A
import qualified Data.ByteString as B
import qualified Data.ByteString.Base64 as B64
import qualified Data.Text.Lazy as TL
import Data.Text.Lazy.Encoding (encodeUtf8)
import Patat.Cleanup (Cleanup)
import qualified Patat.Images.Internal as Internal
import System.Directory (findExecutable)
import System.Environment (lookupEnv)
import System.Process (readProcess)
backend :: Internal.Backend
backend :: Backend
backend = (Config Config -> IO Handle) -> Backend
forall a. FromJSON a => (Config a -> IO Handle) -> Backend
Internal.Backend Config Config -> IO Handle
new
data Config = Config deriving (Config -> Config -> Bool
(Config -> Config -> Bool)
-> (Config -> Config -> Bool) -> Eq Config
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Config -> Config -> Bool
== :: Config -> Config -> Bool
$c/= :: Config -> Config -> Bool
/= :: Config -> Config -> Bool
Eq)
instance A.FromJSON Config where parseJSON :: Value -> Parser Config
parseJSON Value
_ = Config -> Parser Config
forall a. a -> Parser a
forall (m :: * -> *) a. Monad m => a -> m a
return Config
Config
data Pane =
Pane { Pane -> Size
paneSize :: Size
, Pane -> Bool
paneIsActive :: Bool
} deriving (Int -> Pane -> ShowS
[Pane] -> ShowS
Pane -> String
(Int -> Pane -> ShowS)
-> (Pane -> String) -> ([Pane] -> ShowS) -> Show Pane
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Pane -> ShowS
showsPrec :: Int -> Pane -> ShowS
$cshow :: Pane -> String
show :: Pane -> String
$cshowList :: [Pane] -> ShowS
showList :: [Pane] -> ShowS
Show)
instance A.FromJSON Pane where
parseJSON :: Value -> Parser Pane
parseJSON = String -> (Object -> Parser Pane) -> Value -> Parser Pane
forall a. String -> (Object -> Parser a) -> Value -> Parser a
A.withObject String
"Pane" ((Object -> Parser Pane) -> Value -> Parser Pane)
-> (Object -> Parser Pane) -> Value -> Parser Pane
forall a b. (a -> b) -> a -> b
$ \Object
o -> Size -> Bool -> Pane
Pane
(Size -> Bool -> Pane) -> Parser Size -> Parser (Bool -> Pane)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
o Object -> Key -> Parser Size
forall a. FromJSON a => Object -> Key -> Parser a
A..: Key
"size"
Parser (Bool -> Pane) -> Parser Bool -> Parser Pane
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser Bool
forall a. FromJSON a => Object -> Key -> Parser a
A..: Key
"is_active"
data Size =
Size { Size -> Int
sizePixelWidth :: Int
, Size -> Int
sizePixelHeight :: Int
} deriving (Int -> Size -> ShowS
[Size] -> ShowS
Size -> String
(Int -> Size -> ShowS)
-> (Size -> String) -> ([Size] -> ShowS) -> Show Size
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Size -> ShowS
showsPrec :: Int -> Size -> ShowS
$cshow :: Size -> String
show :: Size -> String
$cshowList :: [Size] -> ShowS
showList :: [Size] -> ShowS
Show)
instance A.FromJSON Size where
parseJSON :: Value -> Parser Size
parseJSON = String -> (Object -> Parser Size) -> Value -> Parser Size
forall a. String -> (Object -> Parser a) -> Value -> Parser a
A.withObject String
"Size" ((Object -> Parser Size) -> Value -> Parser Size)
-> (Object -> Parser Size) -> Value -> Parser Size
forall a b. (a -> b) -> a -> b
$ \Object
o -> Int -> Int -> Size
Size
(Int -> Int -> Size) -> Parser Int -> Parser (Int -> Size)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
o Object -> Key -> Parser Int
forall a. FromJSON a => Object -> Key -> Parser a
A..: Key
"pixel_width"
Parser (Int -> Size) -> Parser Int -> Parser Size
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser Int
forall a. FromJSON a => Object -> Key -> Parser a
A..: Key
"pixel_height"
new :: Internal.Config Config -> IO Internal.Handle
new :: Config Config -> IO Handle
new Config Config
config = do
Bool -> Cleanup -> Cleanup
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (Config Config
config Config Config -> Config Config -> Bool
forall a. Eq a => a -> a -> Bool
== Config Config
forall a. Config a
Internal.Auto) (Cleanup -> Cleanup) -> Cleanup -> Cleanup
forall a b. (a -> b) -> a -> b
$ do
Maybe String
termProgram <- String -> IO (Maybe String)
lookupEnv String
"TERM_PROGRAM"
Bool -> Cleanup -> Cleanup
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
unless (Maybe String
termProgram Maybe String -> Maybe String -> Bool
forall a. Eq a => a -> a -> Bool
== String -> Maybe String
forall a. a -> Maybe a
Just String
"WezTerm") (Cleanup -> Cleanup) -> Cleanup -> Cleanup
forall a b. (a -> b) -> a -> b
$ BackendNotSupported -> Cleanup
forall e a. Exception e => e -> IO a
throwIO (BackendNotSupported -> Cleanup) -> BackendNotSupported -> Cleanup
forall a b. (a -> b) -> a -> b
$
String -> BackendNotSupported
Internal.BackendNotSupported String
"TERM_PROGRAM not WezTerm"
Handle -> IO Handle
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Internal.Handle {hDrawImage :: String -> IO Cleanup
Internal.hDrawImage = String -> IO Cleanup
drawImage}
drawImage :: FilePath -> IO Cleanup
drawImage :: String -> IO Cleanup
drawImage String
path = do
ByteString
content <- String -> IO ByteString
B.readFile String
path
String
wez <- IO String
wezExecutable
ByteString
resp <- (String -> ByteString) -> IO String -> IO ByteString
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text -> ByteString
encodeUtf8 (Text -> ByteString) -> (String -> Text) -> String -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Text
TL.pack) (IO String -> IO ByteString) -> IO String -> IO ByteString
forall a b. (a -> b) -> a -> b
$ String -> [String] -> String -> IO String
readProcess String
wez [String
"cli", String
"list", String
"--format", String
"json"] []
let panes :: Maybe [Pane]
panes = (ByteString -> Maybe [Pane]
forall a. FromJSON a => ByteString -> Maybe a
A.decode ByteString
resp :: Maybe [Pane])
Cleanup -> Cleanup
Internal.withEscapeSequence (Cleanup -> Cleanup) -> Cleanup -> Cleanup
forall a b. (a -> b) -> a -> b
$ do
String -> Cleanup
putStr String
"1337;File=inline=1;doNotMoveCursor=1;"
case ByteString -> Either String DynamicImage
decodeImage ByteString
content of
Left String
_ -> () -> Cleanup
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ()
Right DynamicImage
img -> String -> Cleanup
putStr (String -> Cleanup) -> String -> Cleanup
forall a b. (a -> b) -> a -> b
$ Double -> Double -> String
wezArString (DynamicImage -> Double
imageAspectRatio DynamicImage
img) (Maybe [Pane] -> Double
activePaneAspectRatio Maybe [Pane]
panes)
String -> Cleanup
putStr String
":"
ByteString -> Cleanup
B.putStr (ByteString -> ByteString
B64.encode ByteString
content)
Cleanup -> IO Cleanup
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Cleanup
forall a. Monoid a => a
mempty
wezArString :: Double -> Double -> String
wezArString :: Double -> Double -> String
wezArString Double
i Double
p | Double
i Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
< Double
p = String
"width=auto;height=95%;"
| Bool
otherwise = String
"width=100%;height=auto;"
wezExecutable :: IO String
wezExecutable :: IO String
wezExecutable = do
Maybe String
w <- String -> IO (Maybe String)
findExecutable String
"wezterm.exe"
case Maybe String
w of
Maybe String
Nothing -> String -> IO String
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return String
"wezterm"
Just String
x -> String -> IO String
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return String
x
imageAspectRatio :: DynamicImage -> Double
imageAspectRatio :: DynamicImage -> Double
imageAspectRatio DynamicImage
i = DynamicImage -> Double
imgW DynamicImage
i Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ DynamicImage -> Double
imgH DynamicImage
i
where
imgH :: DynamicImage -> Double
imgH = Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Double) -> (DynamicImage -> Int) -> DynamicImage -> Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((forall pixel. Pixel pixel => Image pixel -> Int)
-> DynamicImage -> Int
forall a.
(forall pixel. Pixel pixel => Image pixel -> a)
-> DynamicImage -> a
dynamicMap Image pixel -> Int
forall pixel. Pixel pixel => Image pixel -> Int
forall a. Image a -> Int
imageHeight)
imgW :: DynamicImage -> Double
imgW = Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Double) -> (DynamicImage -> Int) -> DynamicImage -> Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ((forall pixel. Pixel pixel => Image pixel -> Int)
-> DynamicImage -> Int
forall a.
(forall pixel. Pixel pixel => Image pixel -> a)
-> DynamicImage -> a
dynamicMap Image pixel -> Int
forall pixel. Pixel pixel => Image pixel -> Int
forall a. Image a -> Int
imageWidth)
paneAspectRatio :: Pane -> Double
paneAspectRatio :: Pane -> Double
paneAspectRatio Pane
p = Pane -> Double
paneW Pane
p Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Pane -> Double
paneH Pane
p
where
paneH :: Pane -> Double
paneH = Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Double) -> (Pane -> Int) -> Pane -> Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Size -> Int
sizePixelHeight (Size -> Int) -> (Pane -> Size) -> Pane -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pane -> Size
paneSize
paneW :: Pane -> Double
paneW = Int -> Double
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> Double) -> (Pane -> Int) -> Pane -> Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Size -> Int
sizePixelWidth (Size -> Int) -> (Pane -> Size) -> Pane -> Int
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Pane -> Size
paneSize
activePaneAspectRatio :: Maybe [Pane] -> Double
activePaneAspectRatio :: Maybe [Pane] -> Double
activePaneAspectRatio Maybe [Pane]
Nothing = Double
defaultAr
activePaneAspectRatio (Just [Pane]
x) =
case (Pane -> Bool) -> [Pane] -> [Pane]
forall a. (a -> Bool) -> [a] -> [a]
filter Pane -> Bool
paneIsActive [Pane]
x of
[Pane
p] -> Pane -> Double
paneAspectRatio Pane
p
[Pane]
_ -> Double
defaultAr
defaultAr :: Double
defaultAr :: Double
defaultAr = (Double
4 Double -> Double -> Double
forall a. Fractional a => a -> a -> a
/ Double
3 :: Double)