summaryrefslogtreecommitdiffstats
path: root/app/xoutinfo.hs
blob: 4fd944fff004c2d00c9527237f9bd22a6b96f929 (plain)
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
{-# LANGUAGE DuplicateRecordFields #-}
module Main (main) where

import Control.Exception
import Data.Aeson
import Data.ByteString.Lazy.Char8 qualified as L8
import Data.Maybe
import GHC.Generics
import Graphics.X11.Xlib.Atom
import Graphics.X11.Xlib.Display
import Graphics.X11.Xlib.Misc
import Graphics.X11.Xrandr
import System.Environment


data Output =
    Output
      { device_scale_factor :: Float
      , dpi_x :: Float
      , dpi_y :: Float
      , height :: Int
      , height_mm :: Int
      , name :: String
      , width :: Int
      , width_mm :: Int
      }
    deriving (Generic, Show)

instance ToJSON Output

data Override =
    Override
      { height_mm :: Maybe Int
      , width_mm :: Maybe Int
      }
    deriving (Generic, Show)

instance FromJSON Override

emptyOverride :: Override
emptyOverride =
    Override
      { height_mm = Nothing
      , width_mm = Nothing
      }

main :: IO ()
main = do
    let
        -- This assumes DPI has not been configured
        -- (e.g. by passing -dpi to X or calling xrandr --dpi)
        default_dpi = 96
    bracket (getEnv "DISPLAY" >>= openDisplay) closeDisplay $ \dpy -> do
      let w = defaultRootWindow dpy
      (_, _, _, x, y, _, _, _) <- queryPointer dpy w
      filter (isPointedAt x y) . fromJust <$> xrrGetMonitors dpy w True
        >>= mapM (xrrMonitorInfoToOutput dpy default_dpi)
        >>= mapM_ (L8.putStrLn . encode)
  where
    isPointedAt
        x
        y
        XRRMonitorInfo
          { xrr_moninf_x
          , xrr_moninf_y
          , xrr_moninf_width
          , xrr_moninf_height
          } =
      xrr_moninf_x <= x && x < xrr_moninf_x + xrr_moninf_width &&
      xrr_moninf_y <= y && y < xrr_moninf_y + xrr_moninf_height
    xrrMonitorInfoToOutput
        dpy
        default_dpi
        XRRMonitorInfo
          { xrr_moninf_name
          , xrr_moninf_width
          , xrr_moninf_height
          , xrr_moninf_mwidth
          , xrr_moninf_mheight
          } = do
      name <- fromMaybe "???" <$> getAtomName dpy xrr_moninf_name

      Override
        { width_mm = override_width_mm
        , height_mm = override_height_mm
        } <-
        fromMaybe emptyOverride . maybe Nothing (decode . L8.pack)
          <$> lookupEnv "XOUTINFO_OVERRIDE"

      let
          width = fromIntegral xrr_moninf_width
          height = fromIntegral xrr_moninf_height
          width_mm =
            fromMaybe (fromIntegral xrr_moninf_mwidth)
                      override_width_mm
          height_mm =
            fromMaybe (fromIntegral xrr_moninf_mheight)
                      override_height_mm
          dpi_x = fromIntegral width / fromIntegral width_mm * 25.4
          dpi_y = fromIntegral height / fromIntegral height_mm * 25.4
      return Output
        { name = name
        , device_scale_factor = dpi_x / default_dpi
        , dpi_x = dpi_x
        , dpi_y = dpi_y
        , width = width
        , height = height
        , width_mm = width_mm
        , height_mm = height_mm
        }