wmiirc-rumai

git clone git://oldgit.suckless.org/wmiirc-rumai/
Log | Files | Refs | README | LICENSE

config.yaml (24182B)


      1 #
      2 # High-level wmii configuration.
      3 #
      4 # Ruby code in this file has access
      5 # to a CONFIG constant which contains
      6 # the data in this configuration file.
      7 #
      8 #--
      9 # Copyright protects this work.
     10 # See LICENSE file for details.
     11 #++
     12 
     13 
     14 ##
     15 # Appearance settings.
     16 #
     17 display:
     18 
     19   ##
     20   # Where to display the horizontal status bar?
     21   #
     22   # Possible choices are "top" and "bottom".
     23   #
     24   bar: bottom
     25 
     26   ##
     27   # The font to use in all text drawn by wmii.
     28   #
     29   font: -*-fixed-medium-r-*-*-18-*-*-*-*-*-*-*
     30 
     31   ##
     32   # Thickness of client border (measured in pixels).
     33   #
     34   border: 1
     35 
     36   ##
     37   # Number of seconds a notice should be displayed.
     38   #
     39   notice: 5
     40 
     41   ##
     42   # Color schemes for everything drawn by wmii.
     43   #
     44   #   <scheme>: "<text> <background> <border>"
     45   #
     46   # You can find more color schemes here:
     47   #
     48   #   http://wmii.suckless.org/scripts_n_snips/themes
     49   #
     50   color:
     51     normal:   "#c0c0c0 #0a0a0a #202020"
     52     focus:    "#ffffff #285577 #4c7899"
     53     error:    "#8a1f11 #FBE3E4 #FBC2C4" # from http://www.blueprintcss.org
     54     notice:   "#514721 #FFF6BF #FFD324" # from http://www.blueprintcss.org
     55     success:  "#264409 #E6EFC2 #C6D880" # from http://www.blueprintcss.org
     56 
     57   ##
     58   # Color of desktop background.
     59   #
     60   background: "#333333"
     61 
     62   ##
     63   # Settings for columns drawn by wmii.
     64   #
     65   #   mode: <the wmii "colmode" setting>
     66   #   rule: <the wmii "colrules" setting>
     67   #
     68   column:
     69     mode: default
     70     rule: |
     71       /gimp/ -> 17+83+41
     72       /.*/   -> 50+50
     73 
     74   ##
     75   # Mapping of clients to views they must appear on.
     76   #
     77   #   - <client props regular expression> : <tags to apply>
     78   #
     79   # These mappings are processed in top-to-bottom order.
     80   # Processing stops after the first matching mapping is applied.
     81   #
     82   client:
     83     - /\b(xconsole|alsamixer|XMMS|Sonata)\b/            : 1
     84     - /^pidgin:|:WeeChat\b/                             : chat
     85     - /\b(Liferea|GMail|Thunderbird)\b/                 : mail
     86     - /:(Firefox|Shiretoko):.*\bRestore\b.*\bSession\b/ : web
     87 
     88   ##
     89   # Self-refreshing buttons on the status bar.
     90   #
     91   #   - <button name>:
     92   #       refresh:  <number of seconds to wait before refreshing the content>
     93   #       content:  <Ruby code whose result is displayed as the content>
     94   #       click:    <Ruby code to handle mouse clicks on the status button.
     95   #                  This code has access to a "mouse_button" variable which is
     96   #                  an integer representing the mouse button that was clicked.>
     97   #
     98   # You can refresh a particular status button in Ruby using:
     99   #
    100   #   status "your button name"
    101   #
    102   # The horizontal order in which these buttons appear on the status
    103   # bar reflects the vertical order in which they are defined below.
    104   #
    105   status:
    106     - music:
    107         refresh: 15
    108         content: |
    109           unless defined? @music
    110             require 'rubygems'
    111             gem 'librmpd', '~> 0.1'
    112             require 'librmpd'
    113 
    114             @music = MPD.new
    115           end
    116 
    117           unless @music.connected?
    118             @music.connect
    119           end
    120 
    121           music_state = (@music.stopped? || @music.paused?) ? '(-)' : '(>)'
    122 
    123           if song = @music.current_song
    124             artist    = song.artist
    125             title     = song.title || (f = song.file and File.basename(f))
    126             song_name = [artist, title].compact.join(': ')
    127           end
    128 
    129           [music_state, song_name].compact
    130 
    131     - volume:
    132         refresh: 60
    133         content: |
    134           ['volume', `amixer get Master`.scan(/\d+%/).first]
    135 
    136     - disk_space:
    137         refresh: 600 # 10 minutes
    138         content: |
    139           free, used, path = `df -h ~`.split.last(3)
    140           [path, used, 'used', free, 'free']
    141 
    142     - system_load:
    143         refresh: 10
    144         content: |
    145           load_averages = File.read('/proc/loadavg').split.first(3)
    146           current_load  = load_averages.first.to_f
    147 
    148           # visually indicate the intensity of system load
    149           color = case
    150             when current_load > 3.0 then CONFIG['display']['color']['error']
    151             when current_load > 1.5 then CONFIG['display']['color']['notice']
    152           end
    153 
    154           [color, *load_averages]
    155 
    156     - clock:
    157         refresh: 5
    158         content: Time.now.to_s
    159 
    160 
    161 ##
    162 # Interaction settings.
    163 #
    164 control:
    165 
    166   ##
    167   # The wmii "grabmod" setting.
    168   #
    169   grab: Mod1
    170 
    171   ##
    172   # Key bindings.
    173   #
    174   #   <key sequence>: <Ruby code to execute>
    175   #
    176   key:
    177     #---------------------------------------------------------------------------
    178     # focus
    179     #---------------------------------------------------------------------------
    180 
    181     Mod1-Control-t: | # focus above client
    182       curr_view.select(:up) rescue nil
    183 
    184     Mod1-Control-n: | # focus below client
    185       curr_view.select(:down) rescue nil
    186 
    187     Mod1-Control-h: | # focus left client
    188       curr_view.select(:left) rescue nil
    189 
    190     Mod1-Control-s: | # focus right client
    191       curr_view.select(:right) rescue nil
    192 
    193     Mod1-Control-space: | # focus floating area (toggle)
    194       curr_view.select(:toggle)
    195 
    196     Mod1-Control-comma: | # focus previous view
    197       prev_view.focus
    198 
    199     Mod1-Control-period: | # focus next view
    200       next_view.focus
    201 
    202     # focus the view whose index or name equals the pressed number
    203     Mod1-Control-1: focus_view( tags[0] || 1  )
    204     Mod1-Control-2: focus_view( tags[1] || 2  )
    205     Mod1-Control-3: focus_view( tags[2] || 3  )
    206     Mod1-Control-4: focus_view( tags[3] || 4  )
    207     Mod1-Control-5: focus_view( tags[4] || 5  )
    208     Mod1-Control-6: focus_view( tags[5] || 6  )
    209     Mod1-Control-7: focus_view( tags[6] || 7  )
    210     Mod1-Control-8: focus_view( tags[7] || 8  )
    211     Mod1-Control-9: focus_view( tags[8] || 9  )
    212     Mod1-Control-0: focus_view( tags[9] || 10 )
    213 
    214     # focus the view whose name begins with the pressed alphabet
    215     Mod1-Control-v,a: t = tags.grep(/^a/i).first and focus_view(t)
    216     Mod1-Control-v,b: t = tags.grep(/^b/i).first and focus_view(t)
    217     Mod1-Control-v,c: t = tags.grep(/^c/i).first and focus_view(t)
    218     Mod1-Control-v,d: t = tags.grep(/^d/i).first and focus_view(t)
    219     Mod1-Control-v,e: t = tags.grep(/^e/i).first and focus_view(t)
    220     Mod1-Control-v,f: t = tags.grep(/^f/i).first and focus_view(t)
    221     Mod1-Control-v,g: t = tags.grep(/^g/i).first and focus_view(t)
    222     Mod1-Control-v,h: t = tags.grep(/^h/i).first and focus_view(t)
    223     Mod1-Control-v,i: t = tags.grep(/^i/i).first and focus_view(t)
    224     Mod1-Control-v,j: t = tags.grep(/^j/i).first and focus_view(t)
    225     Mod1-Control-v,k: t = tags.grep(/^k/i).first and focus_view(t)
    226     Mod1-Control-v,l: t = tags.grep(/^l/i).first and focus_view(t)
    227     Mod1-Control-v,m: t = tags.grep(/^m/i).first and focus_view(t)
    228     Mod1-Control-v,n: t = tags.grep(/^n/i).first and focus_view(t)
    229     Mod1-Control-v,o: t = tags.grep(/^o/i).first and focus_view(t)
    230     Mod1-Control-v,p: t = tags.grep(/^p/i).first and focus_view(t)
    231     Mod1-Control-v,q: t = tags.grep(/^q/i).first and focus_view(t)
    232     Mod1-Control-v,r: t = tags.grep(/^r/i).first and focus_view(t)
    233     Mod1-Control-v,s: t = tags.grep(/^s/i).first and focus_view(t)
    234     Mod1-Control-v,t: t = tags.grep(/^t/i).first and focus_view(t)
    235     Mod1-Control-v,u: t = tags.grep(/^u/i).first and focus_view(t)
    236     Mod1-Control-v,v: t = tags.grep(/^v/i).first and focus_view(t)
    237     Mod1-Control-v,w: t = tags.grep(/^w/i).first and focus_view(t)
    238     Mod1-Control-v,x: t = tags.grep(/^x/i).first and focus_view(t)
    239     Mod1-Control-v,y: t = tags.grep(/^y/i).first and focus_view(t)
    240     Mod1-Control-v,z: t = tags.grep(/^z/i).first and focus_view(t)
    241 
    242     #---------------------------------------------------------------------------
    243     # move
    244     #---------------------------------------------------------------------------
    245 
    246     Mod1-Control-m,t: | # move grouping toward the top
    247       grouping.each {|c| c.send(:up) rescue nil }
    248 
    249     Mod1-Control-m,n: | # move grouping toward the bottom
    250       grouping.each {|c| c.send(:down) rescue nil }
    251 
    252     Mod1-Control-m,h: | # move grouping toward the left
    253       grouping.each {|c| c.send(:left) rescue nil }
    254 
    255     Mod1-Control-m,s: | # move grouping toward the right
    256       grouping.each {|c| c.send(:right) rescue nil }
    257 
    258     Mod1-Control-m,space: | # move grouping to floating area (toggle)
    259       grouping.each {|c| c.send(:toggle) rescue nil }
    260 
    261     Mod1-Control-m,v: | # move grouping to chosen view
    262       #
    263       # Changes the tag (according to a menu choice) of
    264       # each grouped client and returns the chosen tag.
    265       #
    266       # The +tag -tag idea is from Jonas Pfenniger:
    267       #
    268       #   http://zimbatm.oree.ch/articles/2006/06/15/wmii-3-and-ruby
    269       #
    270       choices = tags.map {|t| [t, "+#{t}", "-#{t}"] }.flatten
    271 
    272       if target = key_menu(choices, 'tag as:')
    273         grouping.each do |c|
    274           case target
    275           when /^\+/ then c.tag $'
    276           when /^\-/ then c.untag $'
    277           else c.tags = target
    278           end
    279         end
    280       end
    281 
    282     Mod1-Control-m,Delete: | # kill all clients in grouping
    283       grouping.each {|c| c.kill }
    284 
    285     # move grouping to the view whose index or name equals the pressed number
    286     Mod1-Control-m,1: grouping.each {|c| c.tags = tags[0] || 1  }
    287     Mod1-Control-m,2: grouping.each {|c| c.tags = tags[1] || 2  }
    288     Mod1-Control-m,3: grouping.each {|c| c.tags = tags[2] || 3  }
    289     Mod1-Control-m,4: grouping.each {|c| c.tags = tags[3] || 4  }
    290     Mod1-Control-m,5: grouping.each {|c| c.tags = tags[4] || 5  }
    291     Mod1-Control-m,6: grouping.each {|c| c.tags = tags[5] || 6  }
    292     Mod1-Control-m,7: grouping.each {|c| c.tags = tags[6] || 7  }
    293     Mod1-Control-m,8: grouping.each {|c| c.tags = tags[7] || 8  }
    294     Mod1-Control-m,9: grouping.each {|c| c.tags = tags[8] || 9  }
    295     Mod1-Control-m,0: grouping.each {|c| c.tags = tags[9] || 10 }
    296 
    297     #---------------------------------------------------------------------------
    298     # swap
    299     #---------------------------------------------------------------------------
    300 
    301     Mod1-Control-w,t: | # swap with above client
    302       curr_client.swap(:up) rescue nil
    303 
    304     Mod1-Control-w,n: | # swap with below client
    305       curr_client.swap(:down) rescue nil
    306 
    307     Mod1-Control-w,h: | # swap with left client
    308       curr_client.swap(:left) rescue nil
    309 
    310     Mod1-Control-w,s: | # swap with right client
    311       curr_client.swap(:right) rescue nil
    312 
    313     # swap current client with the column whose index equals the pressed number
    314     Mod1-Control-w,1: curr_client.swap 1
    315     Mod1-Control-w,2: curr_client.swap 2
    316     Mod1-Control-w,3: curr_client.swap 3
    317     Mod1-Control-w,4: curr_client.swap 4
    318     Mod1-Control-w,5: curr_client.swap 5
    319     Mod1-Control-w,6: curr_client.swap 6
    320     Mod1-Control-w,7: curr_client.swap 7
    321     Mod1-Control-w,8: curr_client.swap 8
    322     Mod1-Control-w,9: curr_client.swap 9
    323     Mod1-Control-w,0: curr_client.swap 10
    324 
    325     #---------------------------------------------------------------------------
    326     # column
    327     #---------------------------------------------------------------------------
    328 
    329     Mod1-Control-z,w: | # apply equal-spacing layout to current column
    330       curr_area.layout = :default
    331 
    332     Mod1-Control-z,Shift-w: | # apply equal-spacing layout to all columns
    333       curr_view.columns.each do |a|
    334         a.layout = :default
    335       end
    336 
    337     Mod1-Control-z,v: | # apply stacked layout to current column
    338       curr_area.layout = 'stack-max'
    339 
    340     Mod1-Control-z,Shift-v: | # apply stacked layout to all columns
    341       curr_view.columns.each do |a|
    342         a.layout = 'stack-max'
    343       end
    344 
    345     Mod1-Control-z,m: | # apply maximized layout to current column
    346       curr_area.layout = 'stack+max'
    347 
    348     Mod1-Control-z,Shift-m: | # apply maximized layout to all columns
    349       curr_view.columns.each do |a|
    350         a.layout = 'stack+max'
    351       end
    352 
    353     #---------------------------------------------------------------------------
    354     # group
    355     #---------------------------------------------------------------------------
    356 
    357     Mod1-Control-g,g: | # toggle current client from grouping
    358       curr_client.group!
    359 
    360     Mod1-Control-g,c: | # add clients in current area to grouping
    361       curr_area.group
    362 
    363     Mod1-Control-g,Shift-c: | # remove clients in current area from grouping
    364       curr_area.ungroup
    365 
    366     Mod1-Control-g,f: | # add clients in floating area to grouping
    367       Area.floating.group
    368 
    369     Mod1-Control-g,Shift-f: | # remove clients in floating area from grouping
    370       Area.floating.ungroup
    371 
    372     Mod1-Control-g,m: | # add clients in managed areas to grouping
    373       curr_view.managed_areas.each {|a| a.group }
    374 
    375     Mod1-Control-g,Shift-m: | # remove clients in managed areas from grouping
    376       curr_view.managed_areas.each {|a| a.ungroup }
    377 
    378     Mod1-Control-g,v: | # add clients in current view to grouping
    379       curr_view.group
    380 
    381     Mod1-Control-g,Shift-v: | # remove clients in current view from grouping
    382       curr_view.ungroup
    383 
    384     Mod1-Control-g,i: | # invert the grouping in the current view
    385       curr_view.group!
    386 
    387     Mod1-Control-g,Shift-i: | # invert the grouping in all views
    388       Rumai.group!
    389 
    390     Mod1-Control-g,n: | # remove all clients everywhere from grouping
    391       Rumai.ungroup
    392 
    393     #---------------------------------------------------------------------------
    394     # detach
    395     #---------------------------------------------------------------------------
    396 
    397     Mod1-Control-d: | # detach grouping from current view
    398       grouping.each do |c|
    399         c.with_tags do
    400           delete curr_tag
    401           push DETACHED_TAG
    402         end
    403       end
    404 
    405     Mod1-Control-Shift-d: | # attach most recently detached client
    406       v = View.new DETACHED_TAG
    407 
    408       if v.exist? and c = v.clients.last
    409         c.with_tags do
    410           delete DETACHED_TAG
    411           push curr_tag
    412         end
    413       end
    414 
    415     #---------------------------------------------------------------------------
    416     # zoom
    417     #---------------------------------------------------------------------------
    418 
    419     Mod1-Control-f: | # zoom client to fullscreen (toggle)
    420       curr_client.fullscreen!
    421 
    422     Mod1-Control-b: | # copy grouping to temporary view
    423       clients = grouping
    424 
    425       unless clients.empty?
    426         # determine new view
    427         if curr_tag =~ ZOOMED_SUFFIX
    428           src, num = $`, $1.to_i
    429           dst = "#{src}~#{num+1}"
    430         else
    431           dst = "#{curr_tag}~1"
    432         end
    433 
    434         # add clients to new view
    435         clients.each {|c| c.tag dst }
    436 
    437         # focus new view
    438         v = View.new dst
    439         v.focus
    440         v.arrange_in_grid
    441 
    442         # propagate focus into new view
    443         clients.first.focus v
    444       end
    445 
    446     Mod1-Control-Shift-b: | # return grouping to original view
    447       clients = grouping
    448 
    449       unless clients.empty?
    450         src = curr_tag
    451 
    452         if src =~ ZOOMED_SUFFIX
    453           # determine new view
    454           dst = $`
    455 
    456           # remove clients from old view
    457           clients.each do |c|
    458             c.with_tags do
    459               delete src
    460 
    461               if empty?
    462                 push dst
    463               else
    464                 dst = last
    465               end
    466             end
    467           end
    468 
    469           # focus new view
    470           v = View.new dst
    471           v.focus
    472 
    473           # propagate focus into original view
    474           clients.first.focus v
    475         end
    476       end
    477 
    478     #---------------------------------------------------------------------------
    479     # arrange
    480     #---------------------------------------------------------------------------
    481 
    482     Mod1-Control-z,t: | # arrange clients in current view like LarsWM does
    483       curr_view.arrange_as_larswm
    484 
    485     Mod1-Control-z,g: | # arrange clients in current view like a grid
    486       curr_view.arrange_in_grid
    487 
    488     Mod1-Control-z,d: | # arrange clients in current view like a diamond
    489       curr_view.arrange_in_diamond
    490 
    491     # apply grid layout with the pressed number of clients per column
    492     Mod1-Control-z,1: curr_view.arrange_in_grid 1
    493     Mod1-Control-z,2: curr_view.arrange_in_grid 2
    494     Mod1-Control-z,3: curr_view.arrange_in_grid 3
    495     Mod1-Control-z,4: curr_view.arrange_in_grid 4
    496     Mod1-Control-z,5: curr_view.arrange_in_grid 5
    497     Mod1-Control-z,6: curr_view.arrange_in_grid 6
    498     Mod1-Control-z,7: curr_view.arrange_in_grid 7
    499     Mod1-Control-z,8: curr_view.arrange_in_grid 8
    500     Mod1-Control-z,9: curr_view.arrange_in_grid 9
    501     Mod1-Control-z,0: curr_view.arrange_in_grid 9999 # make one giant column
    502 
    503     #---------------------------------------------------------------------------
    504     # menu
    505     #---------------------------------------------------------------------------
    506 
    507     Mod1-Control-i: | # run internal action chosen from a menu
    508       if choice = key_menu(actions, 'run action:')
    509         action choice
    510       end
    511 
    512     Mod1-Control-e: | # run external program chosen from a menu
    513       if choice = key_menu(@programs, 'run program:')
    514         launch choice
    515       end
    516 
    517     Mod1-Control-u: | # focus view chosen from a menu
    518       if choice = key_menu(tags, 'show view:')
    519         focus_view choice
    520       end
    521 
    522     Mod1-Control-a: | # focus client chosen from a menu
    523       choices = []
    524 
    525       clients.each_with_index do |c, i|
    526         choices << "%d. [%s] %s" % [i, c[:tags].read, c[:label].read.downcase]
    527       end
    528 
    529       if target = key_menu(choices, 'show client:')
    530         i = target.scan(/\d+/).first.to_i
    531         clients[i].focus
    532       end
    533 
    534     #---------------------------------------------------------------------------
    535     # launcher
    536     #---------------------------------------------------------------------------
    537 
    538     Mod1-Control-x: | # launch a terminal
    539       #
    540       # Launch a new terminal and set its
    541       # working directory to be the same
    542       # as the currently focused terminal.
    543       #
    544       work = ENV['HOME']
    545 
    546       label = curr_client.label.read rescue ''
    547 
    548       # iterate in reverse order because
    549       # paths are usually at end of label
    550       label.split(' ').reverse_each do |s|
    551         path = File.expand_path(s)
    552 
    553         if File.exist? path
    554           unless File.directory? path
    555             path = File.dirname(path)
    556           end
    557 
    558           work = path
    559           break
    560         end
    561       end
    562 
    563       require 'fileutils'
    564       FileUtils.cd work do
    565         launch 'urxvt'
    566       end
    567 
    568     Mod1-Control-k: | # launch a web browser
    569       launch 'firefox'
    570 
    571     Mod1-Control-j: | # launch a file manager
    572       launch 'thunar'
    573 
    574     Mod1-Control-q: | # launch a note taker
    575       launch 'mousepad'
    576 
    577     #---------------------------------------------------------------------------
    578     # music
    579     #---------------------------------------------------------------------------
    580 
    581     Mod1-Control-Prior: | # previous song
    582       @music.previous rescue nil
    583       status 'music'
    584 
    585     Mod1-Control-Next: | # next song
    586       @music.next rescue nil
    587       status 'music'
    588 
    589     Mod1-Control-Return: | # pause song (toggle)
    590       begin
    591         if @music.stopped?
    592           @music.play
    593         else
    594           @music.pause = !@music.paused?
    595         end
    596       rescue
    597         # ignore
    598       end
    599 
    600       status 'music'
    601 
    602     Mod1-Control-Home: | # load a playlist
    603       if list = key_menu(@music.playlists, 'load playlist:')
    604         @music.clear
    605         @music.load list
    606         @music.play
    607       end
    608 
    609     Mod1-Control-End: | # add current song to a playlist
    610       if list = key_menu(@music.playlists, 'add current song to playlist:')
    611         file = File.join(File.expand_path('~/.mpd/playlists'), list + '.m3u')
    612 
    613         songs = File.readlines(file) rescue []
    614         songs << @music.current_song.file
    615         songs.uniq!
    616 
    617         File.open(file, 'w') {|f| f.puts songs }
    618       end
    619 
    620     #---------------------------------------------------------------------------
    621     # volume
    622     #---------------------------------------------------------------------------
    623 
    624     Mod1-Control-Shift-Prior: | # increase volume
    625       system 'amixer set Master 3dB+'
    626       status 'volume'
    627 
    628     Mod1-Control-Shift-Next: | # decrease volume
    629       system 'amixer set Master 3dB-'
    630       status 'volume'
    631 
    632     Mod1-Control-Shift-Return: | # mute volume (toggle)
    633       system 'amixer set Master toggle'
    634       status 'volume'
    635 
    636   ##
    637   # Event handlers.
    638   #
    639   #   <event name>: <Ruby code to execute>
    640   #
    641   # The Ruby code has access to an "argv" variable which
    642   # is a list of arguments that were passed to the event.
    643   #
    644   event:
    645     CreateTag: |
    646       tag = argv[0]
    647       but = fs.lbar[tag]
    648       but.create unless but.exist?
    649       but.write "#{CONFIG['display']['color']['normal']} #{tag}"
    650 
    651     DestroyTag: |
    652       tag = argv[0]
    653       but = fs.lbar[tag]
    654       but.remove if but.exist?
    655 
    656     FocusTag: |
    657       tag = argv[0]
    658       but = fs.lbar[tag]
    659       but.write "#{CONFIG['display']['color']['focus']} #{tag}" if but.exist?
    660 
    661     UnfocusTag: |
    662       tag = argv[0]
    663       but = fs.lbar[tag]
    664       but.write "#{CONFIG['display']['color']['normal']} #{tag}" if but.exist?
    665 
    666     UrgentTag: |
    667       tag = argv[1]
    668       but = fs.lbar[tag]
    669       but.write "#{CONFIG['display']['color']['notice']} #{tag}" if but.exist?
    670 
    671     NotUrgentTag: |
    672       tag = argv[1]
    673       but = fs.lbar[tag]
    674       color = curr_view.id == tag ? 'focus' : 'normal'
    675       but.write "#{CONFIG['display']['color'][color]} #{tag}" if but.exist?
    676 
    677     LeftBarClick: &LeftBarClick |
    678       mouse_button, view_id = argv
    679 
    680       if mouse_button == '1' # primary button
    681         focus_view view_id
    682       end
    683 
    684     ##
    685     # allows the user to drag a file over a
    686     # view button and activate that view while
    687     # still holding on to their dragged file!
    688     #
    689     LeftBarDND: *LeftBarClick
    690 
    691     RightBarClick: |
    692       status_click *argv.reverse
    693 
    694     Unresponsive: |
    695       client_id = argv[0]
    696       client = Client.new(client_id)
    697 
    698       IO.popen('xmessage -nearmouse -file - -buttons Kill,Wait -print', 'w+') do |f|
    699         f.puts 'The following client is not responding.', ''
    700         f.puts client.inspect
    701         f.puts client.label.read
    702 
    703         f.puts '', 'What would you like to do?'
    704         f.close_write
    705 
    706         if f.read.chomp == 'Kill'
    707           client.slay
    708         end
    709       end
    710 
    711     Notice: |
    712       unless defined? @notice_mutex
    713         require 'thread'
    714         @notice_mutex = Mutex.new
    715       end
    716 
    717       Thread.new do
    718         # prevent notices from overwriting each other
    719         @notice_mutex.synchronize do
    720           button = fs.rbar['!notice']
    721           button.create unless button.exist?
    722 
    723           # display the notice
    724           message = argv.join(' ')
    725 
    726           LOG.info message # also log it in case the user is AFK
    727           button.write "#{CONFIG['display']['color']['notice']} #{message}"
    728 
    729           # clear the notice
    730           sleep [1, CONFIG['display']['notice'].to_i].max
    731           button.remove
    732         end
    733       end
    734 
    735     ClientMouseDown: |
    736       client_id, mouse_button = argv
    737 
    738       if mouse_button == '3' # secondary button
    739         client = Client.new(client_id)
    740 
    741         case click_menu %w[stick group fullscreen kill slay], 'client'
    742         when 'stick'      then client.stick!
    743         when 'group'      then client.group!
    744         when 'fullscreen' then client.fullscreen!
    745         when 'kill'       then client.kill
    746         when 'slay'       then client.slay
    747         end
    748       end
    749 
    750   ##
    751   # Internal scripts.
    752   #
    753   #   <action name>: <Ruby code to execute>
    754   #
    755   action:
    756     reload: | # reload this wmii configuration
    757       reload_config
    758 
    759     rehash: | # scan for available programs and actions
    760       @programs = find_programs(ENV['PATH'].squeeze(':').split(':'))
    761 
    762     clear: | # kill all clients
    763       # firefox's restore session feature does not
    764       # work unless the whole process is killed.
    765       system 'killall firefox firefox-bin thunderbird thunderbird-bin'
    766 
    767       # gnome-panel refuses to die by any other means
    768       system 'killall -s TERM gnome-panel'
    769 
    770       Thread.pass until clients.each do |c|
    771         begin
    772           c.focus # XXX: client must be on current view in order to be killed
    773           c.kill
    774         rescue
    775           # ignore
    776         end
    777       end.empty?
    778 
    779     kill: | # kill the window manager only; do not touch the clients!
    780       fs.ctl.write 'quit'
    781 
    782     quit: | # kill both clients and window manager
    783       action 'clear'
    784       action 'kill'
    785 
    786 
    787 ##
    788 # Arbitrary logic.
    789 #
    790 #   script:
    791 #     before: <Ruby code to execute before processing this file>
    792 #     after:  <Ruby code to execute after processing this file>
    793 #
    794 script:
    795   before: |
    796     DETACHED_TAG  = '|'
    797     ZOOMED_SUFFIX = /~(\d+)$/
    798 
    799   after: |
    800     action 'rehash'
    801 
    802     # desktop wallpaper
    803     system 'sh ~/.fehbg'
    804