Bug correcton in links module.
[berthome:berthome.git] / rc.lua
1 require ("awful")               -- Standard awesome library
2 require ("awful.autofocus")     -- <- XXX: I may not need this...
3 require ("awful.rules")
4 require ("beautiful")           -- theme handling
5 require ("naughty")             -- notifications
6
7 local join      = awful.util.table.join
8 local spawn     = awful.util.spawn
9 local configdir = awful.util.getdir ("config")
10
11 -- Themes define colours, icons, and wallpapers
12 beautiful.init (configdir .. "/theme/theme.lua")
13
14 local vicious = require ("vicious")             -- widgets
15 require ("bowl")                -- custom bowls library for keychain notifications
16 require ("keychain")            -- custom keychain library
17 require ("shifty")              -- dynamic tagging
18 require ("menu")
19 require ("scratch")        -- scratchpad (XXX: not the behavior I need actually)
20 require ("submod")         -- submodule hack for loading imported features...
21 -- require ("posix")            -- `setenv' routine
22 subrequire ('awesompd', configdir..'/awesompd') -- mpd client integration.
23
24 --{{{ Variable definitions
25
26 -- Some custom programs are only loaded by awesome: I put 'em in this directory:
27 BIN_DIR                 = configdir .. "/bin/"
28
29 -- This is used later as the default terminal and editor to run.
30 USER                    = os.getenv ("USER")
31 HOME                    = os.getenv ("HOME")
32 HOST                    = os.getenv ("HOST")
33 LANG                    = os.getenv ("LANG") or "en_US.UTF-8"
34 XTERM                   = "urxvt -cr red -bg black -fg white +sb"
35 STANDBY_SCREEN          = BIN_DIR .. "force-dpms-standby"
36 STANDBY_AND_LOCK_SCREEN = BIN_DIR .. "force-dpms-standby lock"
37 -- IRC                  = XTERM .. " -T weechat -e weechat-curses"
38
39 -- See `http://www-verimag.imag.fr/~berthier/file:.emacs.el' for details about
40 -- these functions...
41 ECLIENT                 = "emacsclient -c"
42 ETERM_NEW               = "emacsclient -c -e '(new-shell-buffer)'"
43 ETERM_DEFAULT           = "emacsclient -c -e '(goto-shell-buffer)'"
44 MAIL_READER             = "emacsclient -c -e '(goto-gnus-buffer)'"
45
46 -- Emms global shortcut commands at the window manager level: I use Emms
47 -- commands instead of the mpc ones so that my emms session stay up to date.
48 EMMS_PLAYLIST           = "emacsclient -c -e '(emms-browser)'"
49 EMMS_PREVIOUS           = "emacsclient -e '(emms-previous)'"
50 EMMS_NEXT               = "emacsclient -e '(emms-next)'"
51 EMMS_SEEK_BACKWARD      = "emacsclient -e '(emms-seek-backward)'"
52 EMMS_SEEK_FORWARD       = "emacsclient -e '(emms-seek-forward)'"
53 -- Note those two does not necessitate an open connection between EMMS and MPD:
54 EMMS_VOLUME_RAISE       = "emacsclient -e '(emms-volume-raise)'"
55 EMMS_VOLUME_LOWER       = "emacsclient -e '(emms-volume-lower)'"
56 MPD_PAUSE               = "mpc -q toggle"
57
58 TERM_GEOMETRY           = "180x60+20+20"
59
60 -- Keyboard mapping configuration shortcuts
61 SET_TO_DEFAULT_KEYMAP  = "xmodmap "..HOME.."/.xmodmaprc.default"
62 SET_TO_ADVANCED_KEYMAP = "xmodmap "..HOME.."/.xmodmaprc.advanced"
63 local function setup_advanced_keymap () spawn (SET_TO_ADVANCED_KEYMAP) end
64 local function setup_default_keymap  () spawn (SET_TO_DEFAULT_KEYMAP)  end
65
66 -- System monitor
67 SYSTEM_MONITOR     = XTERM.." -geometry "..TERM_GEOMETRY.." -e htop"
68 SYSTEM_MONITOR_BIS = XTERM.." -geometry "..TERM_GEOMETRY.." -e htop -u "..USER
69
70 -- Sound mixer (since I do not use a widget for now...)
71 SOUND_MIXER = XTERM.." -e alsamixer"
72
73 --}}}
74
75 -- ---
76
77 --{{{ Startup
78 -- if HOST == "hume" and                -- Only on hume...
79 --    os.getenv ("AWESOME_STARTED") ~= "true" then 
80
81 --    posix.setenv ("AWT_TOOLKIT", "MToolkit")
82 --    posix.setenv ("EDITOR", "emacsclient")
83 --    posix.setenv ("ALTERNATE_EDITOR", "")
84 --    posix.setenv ("NNTPSERVER", "news1.open-news-network.org")
85 --    posix.setenv ("AWESOME_STARTED", "true")
86
87 --    awful.util.pread ("gnome-settings-daemon")
88 --    -- spawn ("xmodmap ~/.xmodmaprc")
89 --    spawn ("xrdb -merge ~/.Xdefaults")
90 --    spawn ("gnome-power-manager")
91 --    spawn ("gnome-volume-control-applet")
92 --    spawn ("nm-applet")
93 -- end
94
95 -- Basic utility (see `http://awesome.naquadah.org/wiki/Autostart'):
96 function run_once(cmd)
97    findme = cmd
98    firstspace = cmd:find(" ")
99    if firstspace then
100       findme = cmd:sub(0, firstspace-1)
101    end
102    awful.util.spawn_with_shell("pgrep -u $USER -x " .. findme .. 
103                                " > /dev/null || exec " .. cmd)
104 end
105
106 -- Start some programs (take care some other are started in .xsession)
107 -- awful.util.spawn_with_shell ("xmodmap ~/.xmodmaprc")
108 -- awful.util.spawn_with_shell ("xrdb -merge ~/.Xresources")
109 run_once ("xscreensaver")
110 -- run_once ("xcompmgr")                --naughty's opacity‚Ķ
111 run_once ("mpd")
112
113 -- ---
114
115 -- Default modkey.  Usually, Mod4 is the key with a logo between Control and
116 -- Alt.  If you do not like this or do not have such a key, I suggest you to
117 -- remap Mod4 to another key using xmodmap or other tools.  However, you can use
118 -- another modifier like Mod1, but it may interact with others.
119 modkey = "Mod4"
120 altkey = "Mod1"
121 -- alias...
122 local mk, ak, ck, sk, ek = modkey, altkey, "Control", "Shift", "Escape"
123 --}}}
124
125 --{{{ Layouts
126 -- Table of layouts to cover with awful.layout.inc, order matters.
127 --
128 -- XXX: I really miss a custom tiled layout √† la emacs/ion3. But that would
129 -- require a unified management of empty screen regions, thus a somehow deep
130 -- modification of the awesome API to make it neat.
131 layouts = {
132    -- I never use the other layouts yet... 'cause they don't fit my needs.
133    awful.layout.suit.floating,
134    awful.layout.suit.max,
135 }
136 -- define some helpers...
137 local function next_layout () awful.layout.inc (layouts,  1) end
138 local function prev_layout () awful.layout.inc (layouts, -1) end
139 --}}}
140
141 --{{{ Naughty
142 -- naughty.config.presets.normal.opacity = 0.8
143 -- naughty.config.presets.low.opacity = 0.8
144 -- naughty.config.presets.critical.opacity = 0.8
145 --}}}
146
147 --{{{ Shifty
148 shifty.config.tags = {
149    ["[www]"]   = { position = 1, nopopup = true,  persist = true,  init = true, },
150    ["[emacs]"] = { position = 2, nopopup = true,  persist = true,  init = true, },
151    -- ["[irc]"]   = { position = 3, nopopup = true,  persist = false, init = false, },
152    ["[sys]"]   = { position = 9, nopopup = false, persist = false, init = false, exclusive = true, },
153 }
154 for i = 3, 8 do                 -- XXX: do I need them?
155    shifty.config.tags = join (shifty.config.tags,
156                               { ["["..i.."]"] = { position = i, persist = true, }})
157 end
158 shifty.config.apps = {
159    { match = {"GNU IceCat.*", ".*Vimperator.*", }, tag = "[www]",   },
160    { match = {"emacs.*",                        }, tag = "[emacs]", },
161    -- { match = {".*@europium.*",                       }, tag = "[europium]", },
162    { match = {"htop",                           }, tag = "[sys]",   },
163    -- { match = {"weechat",                     }, tag = "[irc]",   },
164    { match = {"MPlayer",                        }, float = true, ontop = true, },
165    { match = {"Luciole.*",                      }, float = true,    },
166    { match = {"Ediff",                          }, float = true,    },
167    { match = {"wsim.*", "wsnet",                }, float = true, ontop = true, },
168 }
169 shifty.config.defaults = {
170    layout = awful.layout.suit.max,
171    persist = true,
172    nopopup = true,
173    run = function (tag) naughty.notify ({ text = tag.name }) end,
174    floatBars = false,
175 }
176 shifty.init ()
177 --}}}
178
179 --{{{ Menu
180 -- Create a laucher widget and a main menu.
181 --
182 -- XXX: Note this menu is not displayed properly, but as I never use it, I did
183 -- not try to fix that problem...
184 myawesomemenu = {
185    { "manual",  XTERM .. " -e man awesome" },
186    { "restart", awesome.restart },
187    { "quit",    awesome.quit },
188 }
189
190 local dmenu
191 if HOST == "cinto" then
192    dmenu = debian.menu.Debian_menu
193 else
194    dmenu = menu.debian_menu
195 end
196 mymenuitems = {
197    { "awesome", myawesomemenu, beautiful.awesome_icon },
198    { "debian",  dmenu },
199    { "open terminal", XTERM },
200 }
201 mymainmenu = awful.menu ({ items = mymenuitems })
202 --}}}
203
204 --{{{ Wibox
205 -- Create a textclock widget
206 mytextclock = awful.widget.textclock ()
207
208 -- Create a systray
209 mysystray = widget ({ type = "systray" })
210
211 --{{{ Spacers:
212 separator = widget ({ type = "textbox" })
213 separator.text = ""
214 separator.width = 1
215 separator.bg = beautiful.bg_focus
216 --}}}
217
218 --{{{ Awesompd widget:
219 -- XXX: that's just a test for now, sine I primarily use EMMS through the
220 -- commands above... there seems to be some incompatibilities with feedback info
221 -- from mpd...
222 local mwdgt = nil
223 if HOST == "europium" or HOST == "nobelium" then
224    mwdgt = awesompd:create()      -- Create awesompd widget
225    mwdgt.font = "Liberation Mono" -- Set widget font
226    mwdgt.scrolling = true    -- If true, the text in the widget will be scrolled
227    mwdgt.output_size = 30    -- Set the size of widget in symbols
228    mwdgt.update_interval = 10   -- Set the update interval in seconds
229    mwdgt.path_to_icons = configdir.."/awesompd/icons"
230    mwdgt.jamendo_format = awesompd.FORMAT_OGG
231    mwdgt.browser = "icecat"
232    mwdgt.show_album_cover = true
233    mwdgt.album_cover_size = 80
234    mwdgt.mpd_config = HOME .. "/.mpdconf"
235    mwdgt.ldecorator = " "
236    mwdgt.rdecorator = " "
237    mwdgt.servers = { { server = "localhost", port = 6600 }, }
238    local mpdbuttons = {         -- Set the buttons of the widget
239       { "", awesompd.MOUSE_LEFT,        mwdgt:command_playpause()   },
240       { ck, awesompd.MOUSE_SCROLL_UP,   mwdgt:command_prev_track()  },
241       { ck, awesompd.MOUSE_SCROLL_DOWN, mwdgt:command_next_track()  },
242       { "", awesompd.MOUSE_SCROLL_UP,   mwdgt:command_volume_up()   },
243       { "", awesompd.MOUSE_SCROLL_DOWN, mwdgt:command_volume_down() },
244       { "", awesompd.MOUSE_RIGHT,       mwdgt:command_show_menu()   },
245       { "", "XF86AudioLowerVolume",     mwdgt:command_volume_down() },
246       { "", "XF86AudioRaiseVolume",     mwdgt:command_volume_up()   },
247       { mk, "Pause",                    mwdgt:command_playpause()   },
248    }
249    mwdgt:register_buttons (mpdbuttons)
250    mwdgt:run()                  -- Start it now
251 end
252 --}}}
253
254 --{{{ CPU usage:
255 cpugraph = awful.widget.graph ()
256 cpugraph:set_width (32)
257 cpugraph:set_height (14)
258 cpugraph:set_gradient_angle (0)
259 cpugraph:set_background_color (beautiful.bg_normal)
260 cpugraph:set_color ("#FF5656")
261 cpugraph:set_gradient_colors ({ "#FF5656", "#88A175", "#AECF96" })
262 vicious.register (cpugraph, vicious.widgets.cpu, "$1")
263 --}}}
264
265 --{{{ Memory usage:
266 memprogbar = awful.widget.progressbar ()
267 memprogbar:set_width (6)
268 memprogbar:set_height (14)
269 memprogbar:set_vertical (true)
270 memprogbar:set_background_color (beautiful.bg_normal)
271 memprogbar:set_border_color (nil)
272 memprogbar:set_color ("#AECF96")
273 memprogbar:set_gradient_colors ({ "#AECF96", "#88A175", "#FF5656" })
274 vicious.register (memprogbar, vicious.widgets.mem, "$1", 10)
275 --}}}
276
277 --{{{ Per-screen wibox
278 -- Create a wibox for each screen and add it
279 mywibox = {}
280 mypromptbox = {}
281 mylayoutbox = {}
282 mytaglist = {}
283 mytaglist.buttons =
284    join (
285          awful.button ({     }, 1, awful.tag.viewonly),
286          awful.button ({ mk, }, 1, awful.client.movetotag),
287          awful.button ({     }, 3, awful.tag.viewtoggle),
288          awful.button ({ mk, }, 3, awful.client.toggletag),
289          awful.button ({     }, 4, awful.tag.viewprev),
290          awful.button ({     }, 5, awful.tag.viewnext),
291          {})
292
293 local function cli_focus (c)
294    if not c:isvisible () then awful.tag.viewonly (c:tags ()[1]) end
295    client.focus = c
296    c:raise ()
297    -- do not move mouse to client here...
298 end
299
300 mytasklist = {}
301 mytasklist.buttons =
302    join (awful.button ({ }, 1, cli_focus),
303          {})
304
305 local layout_buttons =
306    join (awful.button ({ }, 1, next_layout),
307          awful.button ({ }, 3, prev_layout),
308          awful.button ({ }, 4, next_layout),
309          awful.button ({ }, 5, prev_layout),
310          {})
311
312 -- TODO: I really don't like the prompt at the top of the screen. Maybe I should
313 -- use a kind of dedicated infoline at the bottom of the root window, as in
314 -- ion3....
315
316 for s = 1, screen.count () do
317    -- Create a promptbox for each screen
318    mypromptbox[s] =
319       awful.widget.prompt ({
320                               layout = awful.widget.layout.horizontal.leftright
321                            })
322
323    -- Create an imagebox widget which will contains an icon indicating which
324    -- layout we're using.  We need one layoutbox per screen.
325    mylayoutbox[s] = awful.widget.layoutbox (s)
326    mylayoutbox[s]:buttons (layout_buttons)
327
328    -- Create a taglist widget
329    mytaglist[s] = awful.widget.taglist (s,
330                                         awful.widget.taglist.label.all,
331                                         mytaglist.buttons)
332
333    -- Create a tasklist widget
334    local function currenttags (c)
335       return awful.widget.tasklist.label.currenttags (c, s)
336    end
337    mytasklist[s] = awful.widget.tasklist (currenttags,
338                                           mytasklist.buttons)
339
340    -- Actually create the wibox
341    mywibox[s] = awful.wibox ({ position = "top", screen = s, height = 14, })
342
343    -- Add widgets to the wibox --- note order matters here
344    mywibox[s].widgets = {
345       {                    -- the content of this table is aligned to the left..
346          mytaglist[s],
347          mypromptbox[s],        -- ... and goes left-right:
348          layout = awful.widget.layout.horizontal.leftright
349       },
350       mylayoutbox[s],           -- the remaining is aligned to the right...
351       mytextclock,
352       separator,
353       memprogbar.widget,
354       separator,
355       cpugraph.widget,
356       separator,
357       s == 1 and mysystray or nil,
358       s == 1 and mwdgt and mwdgt.widget or nil,
359       s == 1 and (mysystray or (mwdgt and mwdgt.widget)) and separator or nil,
360       mytasklist[s],            -- ... and goes right-left:
361       layout = awful.widget.layout.horizontal.rightleft
362    }
363 end
364 --}}}
365
366 -- Shifty stuff...
367 shifty.promptbox = mypromptbox
368 --}}}
369
370 --{{{ Mouse bindings
371 local rootbuttons =
372    join (awful.button ({ }, 4, awful.tag.viewnext),
373          awful.button ({ }, 5, awful.tag.viewprev),
374          {})
375 root.buttons (rootbuttons)
376 --}}}
377
378 --{{{ Keychain & bowl initialization
379 bowl.init ({
380               -- enable infolines, appearing 2 seconds after the typing of
381               -- the first prefix keys.
382               use_timers = true,
383               timeout = 2,
384            })
385 keychain.init ({
386                   -- configutation table content should go here: default values
387                   -- I chose are repeated here (@see keychain.init documentation
388                   -- for further details):
389                   escapes = {
390                      keychain.keystroke ( {     }, "Escape" ),
391                      keychain.keystroke ( { mk, }, "g"      ), -- <- "emacs-like"
392                   },
393                })
394 -- Now, setup the default bowl behavior:
395 bowl.default_setup ()
396 --}}}
397
398 --{{{ Various helpers
399
400 --{{{ Misc.
401 local function new_tag (nopopup)
402    return function () shifty.add ({ rel_index = 1, nopopup = nopopup }) end
403 end
404 local function spn (cmd) return function () spawn (cmd) end end
405
406 -- Helper for toggling boolean field named `f' in table `t'.
407 local function toggle (t, f) t[f] = not t[f] end
408
409 --local function hide_bar () mywibox[1].height = mywibox[1].height + 10 end
410 local function topbar_show () 
411    local s = mywibox[mouse.screen].visible
412    mywibox[mouse.screen].visible = true
413    return s
414 end
415 local function topbar_restore_visibility (s)
416    mywibox[mouse.screen].visible = s
417 end
418 local function topbar_toggle_visibility () 
419    toggle (mywibox[mouse.screen], "visible") 
420 end
421
422 -- Other client-related facilities
423 local function cli_toggle_fs (c) toggle (c, "fullscreen") end
424 local function cli_toggle_max_v (c) toggle (c, "maximized_vertical") end
425 local function cli_toggle_max_h (c) toggle (c, "maximized_horizontal") end
426
427 local function cli_swpmaster (c) c:swap (awful.client.getmaster ()) end
428 local function cli_redraw    (c) c:redraw () end
429 local function cli_kill      (c) c:kill () end
430 local function cli_lower     (c) c:lower () end
431 local function cli_raise     (c) client.focus = c; c:raise () end
432 --}}}
433
434 --{{{ Prompts:
435
436 local function prompt (args, textbox, exe_callback, completion_callback,
437                        history_path, history_max, done_callback)
438    local s = topbar_show ()
439    awful.prompt.run (args, textbox, exe_callback, completion_callback,
440                      history_path, history_max, 
441                      function () 
442                         if done_callback then done_callback () end
443                         topbar_restore_visibility (s)
444                      end)
445 end
446
447 local function run_prompt ()
448    local s = topbar_show ()
449    mypromptbox[mouse.screen]:run ()
450    -- topbar_restore_visibility (s)
451 end
452
453 local function lua_prompt ()
454    prompt ({ prompt = "Run Lua code: " },
455            mypromptbox[mouse.screen].widget,
456            awful.util.eval, nil,
457            awful.util.getdir ("cache") .. "/history_eval")
458 end
459
460 -- @see http://awesome.naquadah.org/wiki/Anrxcs_Manual_Prompt
461 local function man_prompt ()
462    local function man (page)
463       --  Use GNU Emacs for manual page display
464       spawn ("emacsclient --eval '(manual-entry \"'" .. page .. "'\")'", false)
465    end
466    prompt ({ prompt = "Manual: " },
467            mypromptbox[mouse.screen].widget,
468            man)
469 end
470
471 --}}}
472
473 --{{{ Client focus
474 local function mouse_to_client (c)
475    if c then
476       local geom = c:geometry ()
477       mouse.coords ({ x = geom.x + 1, y = geom.y + 1 })
478    end
479 end
480
481 local function switch_focus ()
482    awful.client.focus.history.previous ()
483    if client.focus then
484       client.focus:raise ()
485       mouse_to_client (client.focus)
486    end
487 end
488
489 local function focus_byidx (i) return
490    function ()
491       awful.client.focus.byidx (i)
492       if client.focus then
493          client.focus:raise ()
494          mouse_to_client (client.focus)
495       end
496    end
497 end
498
499 local function swap_byidx (i)
500    return function (c) awful.client.swap.byidx (i) end
501 end
502
503 local function focus_relative (i)
504    return function () awful.screen.focus_relative (i) end
505 end
506 --}}}
507
508 --}}}
509
510 --{{{ Part of the configuration that is related to quick link accesses
511 require ("links")
512
513 links.config.prompt = 
514    function (text, fun, cb, cache)
515       prompt ({ prompt = text }, mypromptbox[mouse.screen].widget, 
516               fun, cb, cache)
517    end
518
519 links.config.open_in_term = 
520    function (command, notify)
521       if notify then
522          naughty.notify ({ text = command })
523       end
524       spawn (XTERM.." -geometry "..TERM_GEOMETRY.." -e ".. command)
525    end
526 --}}}
527
528 --{{{ Key bindings
529 -- aliases:
530 local bind = keychain
531 local ksub = keychain.sub
532 globalkeys =
533    join (
534          -- System monitoring:
535          bind ({ mk,     }, ek, spn (SYSTEM_MONITOR)                                  ),
536          bind ({ mk, ak, }, ek, spn (SYSTEM_MONITOR_BIS)                              ),
537
538          -- Manuals:
539          bind ({ mk,         }, "F1", man_prompt                                      ),
540
541          -- Standard programs:
542          bind ({ mk,         }, "F2", spn (ETERM_DEFAULT)                             ),
543          bind ({ mk, ak,     }, "F2", spn (ETERM_NEW)                                 ),
544          bind ({ mk, ak, sk, }, "F2", spn (XTERM)                                     ),
545          bind ({ mk,         }, "F3", run_prompt                                      ),
546          bind ({ mk, ak,     }, "F3", lua_prompt                                      ),
547          -- bind ({ mk,      }, "F4", spn (IRC)                                       ),
548          bind ({ mk,         }, "F5", spn (MAIL_READER)                               ),
549
550          -- Sound mixer:
551          -- bind ({ mk,      }, "F7", spn (SOUND_MIXER)                               ),
552          bind ({ mk,         }, "F7",
553                function ()
554                   scratch.drop (SOUND_MIXER, "bottom", "center", 1, 0.33, false)
555                end                                                                    ),
556
557          -- DPMS:
558          bind ({ mk,         }, "F10", spn (STANDBY_SCREEN)                           ),
559          bind ({ mk, ak,     }, "F10", spn (STANDBY_AND_LOCK_SCREEN)                  ),
560
561          -- Menu: (XXX: That's not what I want... I'd like a prompt with the
562          -- global menu encoded in it...)
563          bind ({ mk,         }, "F12", function () mymainmenu:toggle ()            end),
564
565          -- Misc. prompts (translations, etc.):
566          bind ({ mk,         }, "e", {
567                   ksub ({     }, "f", function () links.find_translation ("enfr", false) end),
568                   ksub ({ mk, }, "f", function () links.find_translation ("enfr", true)  end),
569                   ksub ({     }, "s", function () links.find_synonym ("en", false)       end),
570                   ksub ({ mk, }, "s", function () links.find_synonym ("en", true)        end),
571                }),
572
573          bind ({ mk,         }, "f", {
574                   ksub ({     }, "e", function () links.find_translation ("fren", false) end),
575                   ksub ({ mk, }, "e", function () links.find_translation ("fren", true)  end),
576                   ksub ({     }, "s", function () links.find_synonym ("fr", false)       end),
577                   ksub ({ mk, }, "s", function () links.find_synonym ("fr", true)        end),
578                   ksub ({     }, "c", function () links.find_fr_conj (false)             end),
579                   ksub ({ mk, }, "c", function () links.find_fr_conj (true)              end),
580                }),
581
582          -- Keymaps
583          bind ({ mk,         }, "k", {
584                   ksub ({ mk, }, "a", setup_advanced_keymap                           ),
585                   ksub ({     }, "a", setup_advanced_keymap                           ),
586                   ksub ({ mk, }, "d", setup_default_keymap                            ),
587                   ksub ({     }, "d", setup_default_keymap                            ),
588                }),
589
590          -- Custom link manipulation...
591          bind ({ mk,         }, "o", function () links.query_open_gate6_link (true)  end),
592          bind ({ mk, ak,     }, "o", function () links.query_open_gate6_link (false) end),
593
594          -- Disabled cause I can never use it this way... and `scratch.pad'
595          -- cannot come to the rescue for what I want (maybe a `scratch'
596          -- tag?)... (that would require empty regions management actually, as
597          -- for custom tiling):
598          --
599          -- bind ({ mk,         }, "space",
600          --  function () scratch.drop (XTERM, "bottom", "center", 1, 0.33, true) end),
601
602          -- Emms global shortcuts
603          bind ({ mk, ak,     }, "p",     spn (EMMS_PLAYLIST)                          ),
604          bind ({ mk, ak,     }, "space", spn (MPD_PAUSE)                              ),
605          bind ({ mk, ak,     }, "Prior", spn (EMMS_PREVIOUS)                          ),
606          bind ({ mk, ak,     }, "Next",  spn (EMMS_NEXT)                              ),
607          bind ({ mk, ak,     }, "Left",  spn (EMMS_SEEK_BACKWARD)                     ),
608          bind ({ mk, ak,     }, "Right", spn (EMMS_SEEK_FORWARD)                      ),
609          bind ({ mk, ak,     }, "Up",    spn (EMMS_VOLUME_RAISE)                      ),
610          bind ({ mk, ak,     }, "Down",  spn (EMMS_VOLUME_LOWER)                      ),
611
612          -- Tag management...:
613          bind ({ mk,     }, "Left",   awful.tag.viewprev                              ),
614          bind ({ mk,     }, "Right",  awful.tag.viewnext                              ),
615          bind ({ mk, sk, }, "Left",   shifty.shift_prev                               ),
616          bind ({ mk, sk, }, "Right",  shifty.shift_next                               ),
617          bind ({ mk,     }, "t", {
618                   ksub ({     }, "n", new_tag (false)                                 ),
619                   ksub ({ mk, }, "n", new_tag (true)                                  ),
620                   ksub ({     }, "k", shifty.del                                      ),
621                   ksub ({     }, "r", shifty.rename                                   ),
622                }),
623
624          -- Layout manipulation:
625          bind ({ mk, sk,     }, ",", swap_byidx (-1)                                  ),
626          bind ({ mk, sk,     }, ".", swap_byidx ( 1)                                  ),
627          bind ({ mk, ck,     }, "j", focus_relative ( 1)                              ),
628          bind ({ mk, ck,     }, "k", focus_relative (-1)                              ),
629          bind ({ mk,         }, "Tab", focus_byidx ( 1)                               ),
630          bind ({ mk, sk,     }, "Tab", focus_byidx (-1)                               ),
631          bind ({ mk,         }, "u", awful.client.urgent.jumpto                       ),
632          bind ({ mk,         }, "l", {
633                   ksub ({     }, "l", prev_layout                                     ),
634                   ksub ({ mk, }, "l", next_layout                                     ),
635                   ksub ({     }, ",", prev_layout                                     ),
636                   ksub ({     }, ".", next_layout                                     ),
637                }),
638          bind ({ mk,         }, "d", topbar_toggle_visibility                         ),
639
640          -- Session-mgmt...
641          bind ({ mk, ak,     }, "x", {
642                   ksub ({ mk, ak, }, "r", awesome.restart                             ),
643                   ksub ({ mk, ak, }, "c", awesome.quit                                ),
644                }),
645
646          {})
647
648 -- Client-related bindings:
649 clientkeys =
650    join (
651          bind ({ mk, sk,     }, "f",      cli_toggle_fs                               ),
652          bind ({ mk, ak,     }, "v",      cli_toggle_max_v                            ),
653          bind ({ mk, sk,     }, "v",      cli_toggle_max_v                            ),
654          bind ({ mk, ak,     }, "h",      cli_toggle_max_h                            ),
655          bind ({ mk, sk,     }, "h",      cli_toggle_max_h                            ),
656          bind ({ mk, sk,     }, "Return", cli_swpmaster                               ),
657          bind ({ mk, sk,     }, "space",  awful.client.floating.toggle                ),
658          bind ({ mk, sk,     }, "r",      cli_redraw                                  ),
659          bind ({ mk,         }, "x", {
660                   ksub ({     }, "r", cli_raise                                       ),
661                   ksub ({ mk, }, "r", cli_raise                                       ),
662                   ksub ({     }, "l", cli_lower                                       ),
663                   -- XXX: Problem with the following one, which is also a the
664                   -- beginning of a prefix for global keys. But I still think
665                   -- client keys should have priority over global ones...
666                   ksub ({ mk, }, "l", cli_lower                                       ),
667                   ksub ({ mk, }, "c", cli_kill                                        ),
668                   ksub ({     }, "o", switch_focus                                    ),
669                   -- Bound twice:
670                   ksub ({     }, ",", swap_byidx (-1)                                 ),
671                   ksub ({     }, ".", swap_byidx ( 1)                                 ),
672                   -- I never use them...:
673                   -- ksub ({     }, "2", function (c) awful.tag.incmaster ( 1)     end),
674                   -- ksub ({     }, "3", function (c) awful.tag.incncol   ( 1)     end),
675                   -- XXX: `cli_resize' does not work properly yet (maybe I
676                   -- should avoid using the keygrabber...):
677                   -- ksub ({     }, "m", cli_resize                                   ),
678                   -- ksub ({     }, "r", cli_resize                                   ),
679                }),
680          -- Ibid.
681          -- bind ({ mk, sk,     }, "o",                   awful.client.movetoscreen   ),
682          -- bind ({ mk,         }, "n",      function (c) c.minimized = not c.minimized end),
683          -- bind ({ mk, ak,     }, "Up",     function (c) awful.client.focus.bydirection ("up", c)    end),
684          -- bind ({ mk, ak,     }, "Down",   function (c) awful.client.focus.bydirection ("down", c)  end),
685          -- bind ({ mk, ak,     }, "Left",   function (c) awful.client.focus.bydirection ("left", c)  end),
686          -- bind ({ mk, ak,     }, "Right",  function (c) awful.client.focus.bydirection ("right", c) end),
687          {})
688
689 -- Compute the maximum number of digit we need, limited to 9
690 keynumber = 9
691
692 -- Bind all key numbers to tags.
693 -- Be careful: we use keycodes to make it work on any keyboard layout.
694 -- This should map on the top row of your keyboard, usually 1 to 9.
695 for i = 1, keynumber do
696    globalkeys =
697       join (globalkeys,
698             bind ({ mk, }, "#" .. i + 9, -- view tag
699                   function ()
700                      awful.tag.viewonly (shifty.getpos (i))
701                   end),
702             bind ({ mk, ck, }, "#" .. i + 9, -- toggle tag
703                   function ()
704                      local t = shifty.getpos (i)
705                      t.selected = not t.selected
706                   end),
707             bind ({ mk, sk, }, "#" .. i + 9, -- move client to tag and view
708                   function ()
709                      if client.focus then
710                         local t = shifty.getpos (i)
711                         awful.client.movetotag (t)
712                         awful.tag.viewonly (t)
713                      end
714                   end),
715             bind ({ mk, ck, sk, }, "#" .. i + 9, -- add tag to client
716                   function ()
717                      if client.focus then
718                         awful.client.toggletag (shifty.getpos (i))
719                      end
720                   end),
721             {})
722 end
723
724 -- Setup initial keys
725
726 root.keys (globalkeys)
727 shifty.config.clientkeys = clientkeys
728 --}}}
729
730 --{{{ Various client bindings and general rules.
731 clientbuttons =
732    join (awful.button ({     }, 1, cli_raise),
733          -- TODO: use keygrapbber to move and resize...
734          awful.button ({ mk, }, 1, awful.mouse.client.move),
735          awful.button ({ mk, }, 3, awful.mouse.client.resize),
736          {})
737 awful.rules.rules = {
738    -- All clients will match this rule.
739    {
740       rule = { },
741       properties = {
742          border_width = beautiful.border_width,
743          border_color = beautiful.border_normal,
744          focus = true,
745          keys = clientkeys,
746          buttons = clientbuttons,
747       },
748    },
749 }
750 --}}}
751
752 --{{{ Signals
753 -- Signal function to execute when a new client appears.
754 local function on_client_startup (c, startup)
755    -- No titlebar... (unless I decide to remove the ):
756    -- awful.titlebar.add (c, { modkey = mk })
757
758    -- Enable sloppy focus
759    c:add_signal ("mouse::enter",
760                  function (c)
761                     if awful.layout.get (c.screen) ~= awful.layout.suit.magnifier and
762                        awful.client.focus.filter (c)
763                     then
764                        client.focus = c
765                     end
766                  end)
767
768    if not startup then
769       -- Set the windows at the slave,
770       -- i.e. put it at the end of others instead of setting it master.
771       -- awful.client.setslave (c)
772
773       -- Put windows in a smart way, only if they does not set an initial
774       -- position.
775       if not c.size_hints.user_position and
776          not c.size_hints.program_position
777       then
778          awful.placement.no_overlap (c)
779          awful.placement.no_offscreen (c)
780       end
781    end
782 end
783
784 client.add_signal ("manage",  on_client_startup)
785 client.add_signal ("focus",   function (c) c.border_color = beautiful.border_focus end)
786 client.add_signal ("unfocus", function (c) c.border_color = beautiful.border_normal end)
787 --}}}
788