summaryrefslogtreecommitdiffhomepage
path: root/doc/window-managers.org
blob: db8207cbb39343f6dea106189dbe78ac4abc9982 (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
#+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 [[../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 [[../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
         initThreads
         q <- STM.newTQueueIO @String
         bar <- forkOS $ 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 }
     #+end_src

     Note that xmonad uses blocking Xlib calls in its event loop and isn't
     normally compiled with
     [[https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/using-concurrent.html][the threaded RTS]]
     so an xmobar thread running inside xmonad will suffer from delayed
     updates. It is thus necessary to enable =-threaded= when compiling
     xmonad configuration (=xmonad.hs=), e.g. by using a custom
     =~/.xmonad/build= script.

* Example of 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