aboutsummaryrefslogtreecommitdiff
path: root/src/Internal/LayoutDraw.hs
blob: 78ff59dec10e635aee608653c4afe7fe27fb9362 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
{-# LANGUAGE FlexibleInstances, FlexibleContexts, MultiParamTypeClasses,
ScopedTypeVariables, BangPatterns #-}
module Internal.LayoutDraw where

import System.IO

import Control.Monad.Writer
import XMonad.Layout.Spacing
import System.Process
import Text.Printf
import Control.Arrow
import Control.Exception
import Control.Monad
import Control.Concurrent (threadDelay)

import System.FilePath
import XMonad
import XMonad.StackSet as S
import Data.Maybe
import Data.Foldable
import System.Directory

import Internal.Layout
import Internal.Hash

showLayout :: X (Bool, String, Maybe String)
showLayout = do
    winset <- gets windowset
    let layout = S.layout . S.workspace . S.current $ winset

    layout' <- handleMessage layout (
       SomeMessage $ ModifyWindowBorder (
          const (Border 0 0 0 0)))

    let layout'' = layout'

    (cached, xpm) <-
        case layout'' of
          Just l -> drawXpmIO l
          Nothing -> drawXpmIO layout
    return $ (cached, description layout, Just $ printf "<icon=%s/>" xpm)

pointInRect :: (Dimension, Dimension) -> Rectangle -> Bool
pointInRect (x, y) (Rectangle x' y' w h) =
  x <= (fi x' + fi w) && x >= fi x' && y <= (fi y' + fi h) && y >= fi y'
  where
    fi :: (Integral a, Num b) => a -> b
    fi = fromIntegral

sf :: (Integral a) => a
sf = 1024

drawXpmIO :: (LayoutClass layout Window) => layout Window -> X (Bool, String)
drawXpmIO l = do
  dir <- getXMonadDir

  let shrinkAmt = 4

  let (w, h) = (56 + shrinkAmt, 28 + shrinkAmt)
  let descr = description l
  let iconCacheDir = dir </> "icons" </> "cache"
  let iconPath = iconCacheDir </> (quickHash descr ++ ".xpm")

  let colors = [
        "#cc9a9a", "#cc9999", "#cc8080", "#cc6666",
        "#cc4c4c", "#cc3232", "#cc1818", "#cc0000" ]

  (rects', _) <-
    runLayout
      (Workspace "0" l (differentiate [1 .. 7]))
      (Rectangle 0 0 (w * sf) (h * sf))

  let rects = flip map rects' $ \(_, (Rectangle x y w h)) ->
                Rectangle (x `div` sf) (y `div` sf) (w `div` sf) (h `div` sf)

  liftIO $ do
    exists <- doesFileExist iconPath
    createDirectoryIfMissing True iconCacheDir

    when (not exists) $ do
      let xpmText = drawXpm (w, h) (zip (cycle colors) rects) 4
      writeFile iconPath xpmText

    return (exists, iconPath)

drawXpm :: (Dimension, Dimension) -> [(String, Rectangle)] -> Dimension -> String
drawXpm (w, h) rects' shrinkAmt = execWriter $ do
    tell "/* XPM */\n"
    tell "static char *out[] = {\n"
    forM_ rects' $ \(_, rect) -> do
      tell $ "/* " ++ show rect ++ " */\n"
    tell $ "/* --------------------------- */\n"
    forM_ rects $ \(_, rect) -> do
      tell $ "/* " ++ show rect ++ " */\n"
      
    tell $ printf "\"%d %d %d 1 \",\n" (w - shrinkAmt) (h - shrinkAmt) (length rects + 1)

    let zipRects = (zip ['A' .. 'Z'] rects)

    forM_ zipRects $ \(char, (color, _)) -> do
      tell $ printf "\"%c c %s\",\n" char color
    tell "\"% c None\"a,\n"

    forM_ [0 .. h - 1 - shrinkAmt] $ \y -> do
      tell "\""
      forM_ [0 .. w - 1 - shrinkAmt] $ \x -> 
        (case find (matches x y) zipRects of
          Nothing -> tell "%"
          Just (chr, _) -> tell [chr])
      tell "\""
      when (y /= h - 1 - shrinkAmt) (tell ",")
      tell "\n"
    tell "};"

  where
    matches x y (_, (_, r)) = pointInRect (x, y) r
    rects = map (second (shrink shrinkAmt)) rects'
    guard a b = if a <= shrinkAmt then 1 else b
    shrink amt (Rectangle x y w h) =
      Rectangle
        x
        y
        (guard w $ w - fromIntegral amt)
        (guard h $ h - fromIntegral amt)