summaryrefslogtreecommitdiffhomepage
path: root/doc/window-managers.org
blob: 09f7e3e4772721c383d90872aa89afcc76dd96f5 (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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
#+title: Interfacing with window managers

Listed below are ways to interface xmobar with your window manager of
choice.

*** Property-based Logging
***** =XMonadLog=

      - Aliases to XMonadLog

      - Displays information from xmonad's =_XMONAD_LOG=. You can use
        this by using functions from the [[https://hackage.haskell.org/package/xmonad-contrib-0.16/docs/XMonad-Hooks-DynamicLog.html][XMonad.Hooks.DynamicLog]]
        module. By using the =xmonadPropLog= function in your logHook,
        you can write the the above property. The following shows a
        minimal xmonad configuration that spawns xmobar and then
        writes to the =_XMONAD_LOG= property.

        #+begin_src haskell
          main = do
            spawn "xmobar"
            xmonad $ def
              { logHook = dynamicLogString defaultPP >>= xmonadPropLog
              }
        #+end_src

        This plugin can be used as a sometimes more convenient
        alternative to =StdinReader=. For instance, it allows you to
        (re)start xmobar outside xmonad.

***** =UnsafeXMonadLog=

      - Aliases to UnsafeXMonadLog
      - Displays any text received by xmobar on the =_XMONAD_LOG= atom.
      - Will not do anything to the text received. This means you can pass
        xmobar dynamic actions. Be careful to escape (using =<raw=…>=) or
        remove tags from dynamic text that you pipe through to xmobar in this
        way.

      - Sample usage: Send the list of your workspaces, enclosed by actions
        tags, to xmobar.  This enables you to switch to a workspace when you
        click on it in xmobar!

        #+begin_src shell
          <action=`xdotool key alt+1`>ws1</action> <action=`xdotool key alt+1`>ws2</action>
        #+end_src

      - If you use xmonad, It is advised that you still use =xmobarStrip= for
        the =ppTitle= in your logHook:

        #+begin_src haskell
          myPP = defaultPP { ppTitle = xmobarStrip }
          main = xmonad $ def
            { logHook = dynamicLogString myPP >>= xmonadPropLog
            }
        #+end_src

***** =XPropertyLog PropName=

      - Aliases to =PropName=
      - Reads the X property named by =PropName= (a string) and displays its
        value. The [[https://github.com/jaor/xmobar/raw/master/examples/xmonadpropwrite.hs][examples/xmonadpropwrite.hs script]] in xmobar's distribution
        can be used to set the given property from the output of any other
        program or script.

***** =UnsafeXPropertyLog PropName=

      - Aliases to =PropName=
      - Same as =XPropertyLog= but the input is not filtered to avoid
        injection of actions (cf. =UnsafeXMonadLog=). The program writing the
        value of the read property is responsible of performing any needed
        cleanups.

***** =NamedXPropertyLog PropName Alias=

      - Aliases to =Alias=
      - Same as =XPropertyLog= but a custom alias can be specified.

***** =UnsafeNamedXPropertyLog PropName Alias=

      - Aliases to =Alias=
      - Same as =UnsafeXPropertyLog=, but a custom alias can be specified.

*** Logging via Stdin
***** =StdinReader=

      - Aliases to StdinReader
      - Displays any text received by xmobar on its standard input.
      - Strips actions from the text received. This means you can't pass
        dynamic actions via stdin. This is safer than =UnsafeStdinReader=
        because there is no need to escape the content before passing it to
        xmobar's standard input.

***** =UnsafeStdinReader=

      - Aliases to UnsafeStdinReader
      - Displays any text received by xmobar on its standard input.
      - Similar to [[=UnsafeXMonadLog=][UnsafeXMonadLog]], in the sense that it does not strip any
        actions from the received text, only using =stdin= and not a property
        atom of the root window. Please be equally carefully when using this
        as when using =UnsafeXMonadLog=!

*** Pipe-based Logging
***** =PipeReader "default text:/path/to/pipe" Alias=

      - Reads its displayed output from the given pipe.
      - Prefix an optional default text separated by a colon
      - Expands environment variables in the first argument of syntax =${VAR}=
        or =$VAR=

***** =MarqueePipeReader "default text:/path/to/pipe" (length, rate, sep) Alias=

      - Generally equivalent to PipeReader

      - Text is displayed as marquee with the specified length, rate in 10th
        seconds and separator when it wraps around

        #+begin_src haskell
          Run MarqueePipeReader "/tmp/testpipe" (10, 7, "+") "mpipe"
        #+end_src

      - Expands environment variables in the first argument

***** =BufferedPipeReader Alias [(Timeout, Bool, "/path/to/pipe1"), ..]=

      - Display data from multiple pipes.

      - Timeout (in tenth of seconds) is the value after which the
        previous content is restored i.e. if there was already
        something from a previous pipe it will be put on display
        again, overwriting the current status.

      - A pipe with Timeout of 0 will be displayed permanently, just
        like =PipeReader=

      - The boolean option indicates whether new data for this pipe
        should make xmobar appear (unhide, reveal). In this case, the
        Timeout additionally specifies when the window should be
        hidden again. The output is restored in any case.

      - Use it for OSD-like status bars e.g. for setting the volume or
        brightness:

        #+begin_src haskell
          Run BufferedPipeReader "bpr"
              [ (  0, False, "/tmp/xmobar_window"  )
              , ( 15,  True, "/tmp/xmobar_status"  )
              ]
        #+end_src

        Have your window manager send window titles to
        =/tmp/xmobar_window=.  They will always be shown and not reveal
        your xmobar. Sending some status information to
        =/tmp/xmobar_status= will reveal xmonad for 1.5 seconds and
        temporarily overwrite the window titles.

      - Take a look at [[http://github.com/jaor/xmobar/raw/master/examples/status.sh][examples/status.sh]]

      - Expands environment variables for the pipe path

*** Handle-based Logging
***** =HandleReader Handle Alias=

      - Display data from a Haskell =Handle=

      - This plugin is only useful if you are running xmobar from another
        Haskell program like XMonad.

      - You can use =System.Process.createPipe= to create a pair of =read= &
        =write= Handles. Pass the =read= Handle to HandleReader and write your
        output to the =write= Handle:

        #+begin_src haskell
          (readHandle, writeHandle) <- createPipe
          xmobarProcess <- forkProcess $ xmobar myConfig
                  { commands =
                      Run (HandleReader readHandle "handle") : commands myConfig
                  }
          hPutStr writeHandle "Hello World"
        #+end_src

*** Software Transactional Memory

    When invoking xmobar from other Haskell code it can be easier and
    more performant to use shared memory.  The following plugins
    leverage =Control.Concurrent.STM= to realize these gains for xmobar.

***** =QueueReader (TQueue a) (a -> String) String=

      - Display data from a Haskell =TQueue a=.

      - This plugin is only useful if you are running xmobar from another
        haskell program like xmonad.

      - You should make an =IO= safe =TQueue a= with
        =Control.Concurrent.STM.newTQueueIO=.  Write to it from the user
        code with =writeTQueue=, and read with =readTQueue=.  A common use
        is to overwite =ppOutput= from =XMonad.Hooks.DynamicLog= as shown
        below.

        #+begin_src haskell
          main :: IO ()
          main = do
            q <- STM.newTQueueIO @String
            bar <- forkIO $ xmobar myConf
              { commands = Run (QueueReader q id "XMonadLog") : commands myConf }
            xmonad $ def { logHook = logWorkspacesToQueue q }

          logWorkspacesToQueue :: STM.TQueue String -> X ()
          logWorkspacesToQueue q =
            dynamicLogWithPP def { ppOutput = STM.atomically . STM.writeTQueue q }
            where
              -- Manage the PrettyPrinting configuration here.
              ppLayout' :: String -> String
              ppLayout' "Spacing Tall"        = xpm "layout-spacing-tall"
              ppLayout' "Spacing Mirror Tall" = xpm "layout-spacing-mirror"
              ppLayout' "Spacing Full"        = xpm "layout-full"
              ppLayout' x = x

              icon :: String -> String
              icon path = "<icon=" ++ path ++ "/>"

              xpm :: String -> String
              xpm = icon . (++ ".xpm")
        #+end_src
*** Example for using the DBus IPC interface with XMonad

    Bind the key which should {,un}map xmobar to a dummy value. This is
    necessary for {,un}grabKey in xmonad.

    #+begin_src haskell
      ((0, xK_Alt_L), pure ())
    #+end_src

    Also, install =avoidStruts= layout modifier from
    =XMonad.Hooks.ManageDocks=

    Finally, install these two event hooks (=handleEventHook= in =XConfig=)
    =myDocksEventHook= is a replacement for =docksEventHook= which reacts
    on unmap events as well (which =docksEventHook= doesn't).

    #+begin_src haskell
      import qualified XMonad.Util.ExtensibleState as XS

      data DockToggleTime = DTT { lastTime :: Time } deriving (Eq, Show, Typeable)

      instance ExtensionClass DockToggleTime where
          initialValue = DTT 0

      toggleDocksHook :: Int -> KeySym -> Event -> X All
      toggleDocksHook to ks ( KeyEvent { ev_event_display = d
                                      , ev_event_type    = et
                                      , ev_keycode       = ekc
                                      , ev_time          = etime
                                      } ) =
              io (keysymToKeycode d ks) >>= toggleDocks >> return (All True)
          where
          toggleDocks kc
              | ekc == kc && et == keyPress = do
                  safeSendSignal ["Reveal 0", "TogglePersistent"]
                  XS.put ( DTT etime )
              | ekc == kc && et == keyRelease = do
                  gap <- XS.gets ( (-) etime . lastTime )
                  safeSendSignal [ "TogglePersistent"
                              , "Hide " ++ show (if gap < 400 then to else 0)
                              ]
              | otherwise = return ()

          safeSendSignal s = catchX (io $ sendSignal s) (return ())
          sendSignal    = withSession . callSignal
          withSession mc = connectSession >>= \c -> callNoReply c mc >> disconnect c
          callSignal :: [String] -> MethodCall
          callSignal s = ( methodCall
                          ( objectPath_    "/org/Xmobar/Control" )
                          ( interfaceName_ "org.Xmobar.Control"  )
                          ( memberName_    "SendSignal"          )
                      ) { methodCallDestination = Just $ busName_ "org.Xmobar.Control"
                          , methodCallBody        = map toVariant s
                          }

      toggleDocksHook _ _ _ = return (All True)

      myDocksEventHook :: Event -> X All
      myDocksEventHook e = do
          when (et == mapNotify || et == unmapNotify) $
              whenX ((not `fmap` (isClient w)) <&&> runQuery checkDock w) refresh
          return (All True)
          where w  = ev_window e
              et = ev_event_type e
    #+end_src