packages feed

ymonad-0.1.0.0: src/YMonad/Core/Common.hs

module YMonad.Core.Common (
  WorkspaceId (..),
  ScreenId (..),
  ViewId (..),
  OutputId (..),
  Point (..),
  Size (..),
  Rect (..),
  Dimension (..),
  RgbaColor (..),
  TitleBarMode (..),
  opaqueRgb8,
  parseColorString,
) where

import Data.Char qualified as Char
import Data.List qualified as List
import Data.Map.Strict qualified as M
import Data.Text qualified as T
import Numeric (readHex)

newtype WorkspaceId = WorkspaceId {unWorkspaceId :: Text}
  deriving stock (Eq, Ord, Show, Read)

newtype ScreenId = ScreenId {unScreenId :: Int}
  deriving stock (Eq, Ord, Show, Read)

newtype ViewId = ViewId {unViewId :: Int}
  deriving stock (Eq, Ord, Show, Read)

newtype OutputId = OutputId {unOutputId :: Int}
  deriving stock (Eq, Ord, Show, Read)

newtype Dimension = Dimension {unDimension :: Int}
  deriving stock (Eq, Ord, Show, Read)
  deriving newtype (Num)

data Point = Point
  { pointX :: Int
  , pointY :: Int
  }
  deriving stock (Eq, Ord, Show, Read)

data Size = Size
  { sizeWidth :: Int
  , sizeHeight :: Int
  }
  deriving stock (Eq, Ord, Show, Read)

data Rect = Rect
  { rectX :: Int
  , rectY :: Int
  , rectWidth :: Int
  , rectHeight :: Int
  }
  deriving stock (Eq, Ord, Show, Read)

data RgbaColor = RgbaColor
  { rgbaRed :: Word32
  , rgbaGreen :: Word32
  , rgbaBlue :: Word32
  , rgbaAlpha :: Word32
  }
  deriving stock (Eq, Ord, Show, Read)

data TitleBarMode
  = NoTitleBar
  | ServerSideTitleBar
  deriving stock (Eq, Ord, Show, Read)

opaqueRgb8 :: Word8 -> Word8 -> Word8 -> RgbaColor
opaqueRgb8 red green blue = rgbaFrom8Bit red green blue 255

parseColorString :: String -> Maybe RgbaColor
parseColorString raw =
  case List.stripPrefix "#" text of
    Just digits -> parseHexColor digits
    Nothing -> namedColor text
  where
    text = map Char.toLower raw

namedColor :: String -> Maybe RgbaColor
namedColor name = do
  (red, green, blue) <- M.lookup (T.pack name) namedPalette
  pure (opaqueRgb8 red green blue)

namedPalette :: Map Text (Word8, Word8, Word8)
namedPalette =
  M.fromList
    [ ("black", (0, 0, 0))
    , ("white", (255, 255, 255))
    , ("red", (255, 0, 0))
    , ("green", (0, 255, 0))
    , ("blue", (0, 0, 255))
    , ("gray", (128, 128, 128))
    , ("grey", (128, 128, 128))
    , ("yellow", (255, 255, 0))
    , ("magenta", (255, 0, 255))
    , ("cyan", (0, 255, 255))
    ]

parseHexColor :: String -> Maybe RgbaColor
parseHexColor digits =
  case digits of
    [r, g, b] -> rgbaFrom8Bit <$> dupHex r <*> dupHex g <*> dupHex b <*> pure 255
    [r, g, b, a] -> rgbaFrom8Bit <$> dupHex r <*> dupHex g <*> dupHex b <*> dupHex a
    [r1, r2, g1, g2, b1, b2] -> rgbaFrom8Bit <$> byte [r1, r2] <*> byte [g1, g2] <*> byte [b1, b2] <*> pure 255
    [r1, r2, g1, g2, b1, b2, a1, a2] -> rgbaFrom8Bit <$> byte [r1, r2] <*> byte [g1, g2] <*> byte [b1, b2] <*> byte [a1, a2]
    _other -> Nothing
  where
    dupHex ch = byte [ch, ch]
    byte chars = case readHex chars of
      [(value, "")] -> Just (fromIntegral (value :: Int))
      _ -> Nothing

rgbaFrom8Bit :: Word8 -> Word8 -> Word8 -> Word8 -> RgbaColor
rgbaFrom8Bit red green blue alpha =
  RgbaColor
    { rgbaRed = expand8Bit (premultiply red alpha)
    , rgbaGreen = expand8Bit (premultiply green alpha)
    , rgbaBlue = expand8Bit (premultiply blue alpha)
    , rgbaAlpha = expand8Bit alpha
    }

expand8Bit :: Word8 -> Word32
expand8Bit value = fromIntegral value * 0x01010101

premultiply :: Word8 -> Word8 -> Word8
premultiply channel alpha =
  fromIntegral (((fromIntegral channel :: Word32) * fromIntegral alpha + 127) `div` 255)