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