window-utils-0.2.1.0: src/OS/Window/X11.hs
module OS.Window.X11 (
Window, -- it's important that the implementation is hidden here, since it will vary between platforms
findByName,
setTitle,
setIcon,
) where
import Codec.Picture
import Control.Applicative
import Control.Arrow
import Data.Bits
import Data.ByteString (ByteString)
import Data.ByteString qualified as BS
import Data.List
import Data.Text (Text)
import Data.Text qualified as T
import Data.Text.Encoding
import Data.Traversable
import Data.Vector.Storable qualified as Vec
import Data.Word
import Graphics.X11 hiding (Window)
import Graphics.X11 qualified as X11
import Graphics.X11.Xlib.Extras
data Window = Window X11.Window Display
deriving (Eq, Ord, Show)
findByName ::
-- | substring which must appear in the window title
Text ->
IO Window
findByName name = do
d <- openDisplay ""
Just (w, _) <- do
nET_CLIENT_LIST <- internAtom d "_NET_CLIENT_LIST" True
Just ids <- getWindowProperty32 d nET_CLIENT_LIST (defaultRootWindow d)
ws <- for ids \(fromIntegral -> i) -> do
Just cs <- getWindowProperty8 d wM_NAME i
pure (i, decodeLatin1 . BS.pack $ map fromIntegral cs)
pure $ find ((name ==) . snd) ws <|> find ((name `T.isInfixOf`) . snd) ws
pure $ Window w d
setTitle :: Window -> Text -> IO ()
setTitle (Window w d) t = do
nET_WM_NAME <- internAtom d "_NET_WM_NAME" True
uTF8_STRING <- internAtom d "UTF8_STRING" True
changeProperty8 d w nET_WM_NAME uTF8_STRING propModeReplace . map fromIntegral . BS.unpack $ encodeUtf8 t
flush d
setIcon ::
Window ->
-- | PNG image
ByteString ->
IO ()
setIcon (Window w d) =
decodePng >>> either error \case
ImageRGBA8 Image{..} -> rgb imageWidth imageHeight imageData \case
r : g : b : a : ps -> Just ((r, g, b, a), ps)
[] -> Nothing
_ -> error "vector length not a multiple of 4"
ImageRGB8 Image{..} -> rgb imageWidth imageHeight imageData \case
r : g : b : ps -> Just ((r, g, b, maxBound), ps)
[] -> Nothing
_ -> error "vector length not a multiple of 3"
ImageY8{} -> error "unexpected pixel type: ImageY8"
ImageY16{} -> error "unexpected pixel type: ImageY16"
ImageY32{} -> error "unexpected pixel type: ImageY32"
ImageYF{} -> error "unexpected pixel type: ImageYF"
ImageYA8{} -> error "unexpected pixel type: ImageYA8"
ImageYA16{} -> error "unexpected pixel type: ImageYA16"
ImageRGB16{} -> error "unexpected pixel type: ImageRGB16"
ImageRGBF{} -> error "unexpected pixel type: ImageRGBF"
ImageRGBA16{} -> error "unexpected pixel type: ImageRGBA16"
ImageYCbCr8{} -> error "unexpected pixel type: ImageYCbCr8"
ImageCMYK8{} -> error "unexpected pixel type: ImageCMYK8"
ImageCMYK16{} -> error "unexpected pixel type: ImageCMYK16"
where
rgb :: Int -> Int -> Vec.Vector Word8 -> ([Word8] -> Maybe ((Word8, Word8, Word8, Word8), [Word8])) -> IO ()
rgb imageWidth imageHeight imageData unconsPixels = do
nET_WM_ICON <- internAtom d "_NET_WM_ICON" True
changeProperty32 d w nET_WM_ICON cARDINAL propModeReplace $
map fromIntegral [imageWidth, imageHeight]
++ map fromIntegral (groupPixels $ Vec.toList imageData)
flush d
where
groupPixels :: [Word8] -> [Word64]
groupPixels =
unconsPixels >>> maybe [] \((r, g, b, a), ps) ->
( shift (fromIntegral a) 24
.|. shift (fromIntegral r) 16
.|. shift (fromIntegral g) 8
.|. shift (fromIntegral b) 0
)
: groupPixels ps