rc.lua 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875
  1. --[[
  2. Copyright 2017 Stefano Mazzucco <stefano AT curso DOT re>
  3. This program is free software: you can redistribute it and/or modify it under
  4. the terms of the GNU General Public License as published by the Free Software
  5. Foundation, either version 3 of the License, or (at your option) any later
  6. version.
  7. This program is distributed in the hope that it will be useful, but WITHOUT
  8. ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. details.
  11. You should have received a copy of the GNU General Public License along with
  12. this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
  13. ]]
  14. --- My personal Awesome Configuration
  15. -- Awesome Version: 4.x
  16. -- Standard awesome library
  17. local awesome = awesome -- luacheck: ignore
  18. local root = root -- luacheck: ignore
  19. local screen = screen -- luacheck: ignore
  20. local client = client -- luacheck: ignore
  21. local instance = instance -- luacheck: ignore
  22. local mouse = mouse -- luacheck: ignore
  23. local gears = require("gears")
  24. local awful = require("awful")
  25. require("awful.autofocus")
  26. -- Widget and layout library
  27. local wibox = require("wibox")
  28. -- Theme handling library
  29. local beautiful = require("beautiful")
  30. local dpi = require("beautiful.xresources").apply_dpi
  31. local gtk_settings = require("lgi").Gtk.Settings.get_default()
  32. -- Notification library
  33. local naughty = require("naughty")
  34. local menubar = require("menubar")
  35. local hotkeys_popup = require("awful.hotkeys_popup").widget
  36. local media_players = require("media_players")
  37. --[[
  38. gears.cycle_value is not present in awesome 4.3 graft it to make the
  39. layoutlist example work
  40. - https://awesomewm.org/doc/api/classes/awful.widget.layoutlist.html
  41. - https://github.com/awesomeWM/awesome/pull/2942/files
  42. - https://github.com/awesomeWM/awesome/issues/2869
  43. ]]
  44. --- Get the next (or previous) value from a table and cycle if necessary.
  45. --
  46. -- If the table contains the same value multiple type (aka, is not a set), the
  47. -- `first_index` has to be specified.
  48. --
  49. -- @tparam table tbl The input table.
  50. -- @param value A value from the table.
  51. -- @tparam[opt=1] number step_size How many element forward (or backward) to pick.
  52. -- @tparam[opt=nil] function filter An optional function. When it returns
  53. -- `false`, the element are skipped until a match if found. It takes the value
  54. -- as its sole parameter.
  55. -- @tparam[opt=1] number start_at Where to start the lookup from.
  56. -- @return The value. If no element match, then `nil` is returned.
  57. -- @treturn number|nil The element (if any) key.
  58. -- @staticfct gears.table.cycle_value
  59. function cycle_value(tbl, value, step_size, filter, start_at)
  60. local k = gears.table.hasitem(tbl, value, true, start_at)
  61. if not k then return end
  62. step_size = step_size or 1
  63. local new_key = gears.math.cycle(#tbl, k + step_size)
  64. if filter and not filter(tbl[new_key]) then
  65. for i=1, #tbl, step_size do
  66. local k2 = gears.math.cycle(#tbl, new_key + i)
  67. if filter(tbl[k2]) then
  68. return tbl[k2], k2
  69. end
  70. end
  71. return
  72. end
  73. return tbl[new_key], new_key
  74. end
  75. gears.table.cycle_value = gears.table.cycle_value or cycle_value
  76. -- Quodlibet
  77. local quodlibet = media_players.quodlibet
  78. -- Vlc
  79. local vlc = media_players.vlc
  80. -- This is used later as the default terminal and editor to run.
  81. local terminal = "terminology"
  82. local Quake = require("quake")
  83. local quake_name = "QuakeTerminal"
  84. local quake_command = terminal .. " -n " .. quake_name
  85. local quake = Quake:new({
  86. name = quake_name,
  87. cmd = quake_command,
  88. client_props = {
  89. maximized_horizontal = true,
  90. maximized = false,
  91. }
  92. })
  93. local function quake_start()
  94. awful.spawn.easy_async(
  95. 'pgrep -c -f "' .. quake_command .. '"',
  96. function (stdout, _, _, exitcode)
  97. if exitcode ~= 0 then
  98. quake:spawn()
  99. naughty.notify({title = "Quake Terminal started"})
  100. end
  101. local nterminals = tonumber(stdout)
  102. if nterminals > 1 then
  103. naughty.notify({
  104. preset = naughty.config.presets.critical,
  105. title = "Too Many Quake Terminals",
  106. text = nterminals .. " terminals have been spawned"
  107. })
  108. end
  109. end
  110. )
  111. end
  112. local function make_kill(signal, message)
  113. return function (c)
  114. local pid = c.pid
  115. if not pid then return end
  116. awful.spawn.easy_async(
  117. "kill " .. signal .. " " .. pid,
  118. function (_, stderr, _, exitcode)
  119. if exitcode ~= 0 then
  120. naughty.notify({
  121. title = string.format(
  122. "%s %s (FAILURE)",
  123. message,
  124. c.name
  125. ),
  126. text = stderr,
  127. preset = naughty.config.preset.critical
  128. })
  129. else
  130. naughty.notify({
  131. title = string.format(
  132. "%s %s",
  133. message,
  134. c.name
  135. )
  136. })
  137. end
  138. end)
  139. end
  140. end
  141. local suspend = make_kill("-STOP", "Suspend")
  142. local resume = make_kill("-CONT", "Resume")
  143. local function lock_screen()
  144. awful.spawn("xset s activate")
  145. end
  146. local function toggle_suspend()
  147. local c = client.focus
  148. if c ~=nil then
  149. local check_state_cmd = string.format("ps -q %s -o state=", c.pid)
  150. awful.spawn.easy_async(
  151. check_state_cmd,
  152. function (stdout, stderr, _, exitcode)
  153. if exitcode == 0 then
  154. if stdout:match("^S") then
  155. suspend(c)
  156. c.name = "[SUSPENDED] " .. c.name
  157. elseif stdout:match("^T") then
  158. resume(c)
  159. c.name = c.name:gsub("%[SUSPENDED%] ", "")
  160. end
  161. else
  162. naughty.notify({
  163. title = string.format(
  164. "Failed to get state information for %s",
  165. c.name
  166. ),
  167. text = stderr,
  168. preset = naughty.config.presets.critical
  169. })
  170. end
  171. end)
  172. end
  173. end
  174. naughty.config.defaults.position = "top_middle"
  175. -- {{{ Error handling
  176. -- Check if awesome encountered an error during startup and fell back to
  177. -- another config (This code will only ever execute for the fallback config)
  178. if awesome.startup_errors then
  179. naughty.notify({ preset = naughty.config.presets.critical,
  180. title = "Oops, there were errors during startup!",
  181. text = awesome.startup_errors })
  182. end
  183. -- Handle runtime errors after startup
  184. do
  185. local in_error = false
  186. awesome.connect_signal("debug::error", function (err)
  187. -- Make sure we don't go into an endless error loop
  188. if in_error then return end
  189. in_error = true
  190. naughty.notify({ preset = naughty.config.presets.critical,
  191. title = "Oops, an error happened!",
  192. text = tostring(err) })
  193. in_error = false
  194. end)
  195. end
  196. -- }}}
  197. -- {{{ Variable definitions
  198. -- Themes define colours, icons, font and wallpapers.
  199. local theme = dofile(gears.filesystem.get_themes_dir() .. "gtk/theme.lua")
  200. theme.icon_theme = gtk_settings.gtk_icon_theme_name
  201. theme.font = gtk_settings.gtk_font_name
  202. -- https://awesomewm.org/recipes/awesome-taglist/
  203. theme.taglist_font = "awesomewm-font 13"
  204. theme.useless_gap = dpi(0)
  205. beautiful.init(theme)
  206. beautiful.notification_icon_size = 64
  207. quake_start()
  208. -- require *after* `beautiful.init` or the theme will be inconsistent!
  209. local capslock = require("external.capslock.capslock")
  210. local pulse = require("pulseaudio_widget")
  211. local connman = require("connman_widget")
  212. local power = require("power_widget")
  213. power.critical_percentage = 9
  214. power.warning_config = {
  215. percentage = 21,
  216. preset = {
  217. shape = gears.shape.rounded_rect,
  218. bg = "#FFFF00",
  219. fg = "#000000",
  220. timeout = 3,
  221. },
  222. message = "The battery is getting low",
  223. }
  224. local calendar = require("external.calendar")
  225. -- Default modkey.
  226. -- Usually, Mod4 is the key with a logo between Control and Alt.
  227. -- If you do not like this or do not have such a key,
  228. -- I suggest you to remap Mod4 to another key using xmodmap or other tools.
  229. -- However, you can use another modifier like Mod1, but it may interact with others.
  230. local modkey = "Mod4"
  231. do
  232. -- https://awesomewm.org/doc/api/classes/awful.widget.layoutlist.html
  233. awful.layout.layouts = {
  234. -- Table of layouts to cover with awful.layout.inc, order matters.
  235. awful.layout.suit.spiral.dwindle,
  236. awful.layout.suit.tile,
  237. awful.layout.suit.tile.left,
  238. awful.layout.suit.tile.bottom,
  239. awful.layout.suit.tile.top,
  240. awful.layout.suit.fair,
  241. awful.layout.suit.fair.horizontal,
  242. awful.layout.suit.floating,
  243. awful.layout.suit.spiral,
  244. awful.layout.suit.max,
  245. awful.layout.suit.max.fullscreen,
  246. awful.layout.suit.magnifier
  247. }
  248. local layout_list = awful.widget.layoutlist {
  249. base_layout = wibox.widget {
  250. spacing = 5,
  251. forced_num_cols = 5,
  252. layout = wibox.layout.grid.vertical,
  253. },
  254. widget_template = {
  255. {
  256. {
  257. id = 'icon_role',
  258. forced_height = 22,
  259. forced_width = 22,
  260. widget = wibox.widget.imagebox,
  261. },
  262. margins = 4,
  263. widget = wibox.container.margin,
  264. },
  265. id = 'background_role',
  266. forced_width = 24,
  267. forced_height = 24,
  268. shape = gears.shape.rounded_rect,
  269. widget = wibox.container.background,
  270. },
  271. }
  272. local layout_popup = awful.popup {
  273. widget = wibox.widget {
  274. layout_list,
  275. margins = 4,
  276. widget = wibox.container.margin,
  277. },
  278. border_color = beautiful.border_color,
  279. border_width = beautiful.border_width,
  280. placement = awful.placement.centered,
  281. ontop = true,
  282. visible = false,
  283. shape = gears.shape.rounded_rect
  284. }
  285. awful.keygrabber { -- https://awesomewm.org/doc/api/classes/awful.keygrabber.html
  286. start_callback = function() layout_popup.visible = true end,
  287. stop_callback = function()
  288. layout_popup.visible = false
  289. layout_popup._do_cycle = false -- custom attribute to signal whether to cycle layouts
  290. end,
  291. export_keybindings = true,
  292. release_event = "release",
  293. stop_key = {"Escape", "Super_L", "Super_R", "Return"},
  294. keybindings = {
  295. {
  296. {modkey}, " " ,
  297. function()
  298. if not layout_popup._do_cycle then
  299. layout_popup._do_cycle = true
  300. else
  301. awful.layout.set(gears.table.cycle_value(layout_list.layouts, layout_list.current_layout, 1), nil)
  302. end
  303. end
  304. },
  305. {
  306. {modkey, "Shift"} , " " ,
  307. function()
  308. if not layout_popup._do_cycle then
  309. layout_popup._do_cycle = true
  310. else
  311. awful.layout.set(gears.table.cycle_value(layout_list.layouts, layout_list.current_layout, -1), nil)
  312. end
  313. end
  314. },
  315. }
  316. }
  317. end
  318. -- {{{ Helper functions
  319. local function client_menu_toggle_fn()
  320. local inst = nil
  321. return function ()
  322. if inst and inst.wibox.visible then
  323. inst:hide()
  324. inst = nil
  325. else
  326. inst = awful.menu.clients({ theme = { width = 250 } })
  327. end
  328. end
  329. end
  330. -- }}}
  331. -- {{{ Menu
  332. -- Menubar configuration
  333. menubar.utils.terminal = terminal -- Set the terminal for applications that require it
  334. -- }}}
  335. -- {{{ Wibar
  336. -- Create a textclock widget
  337. local mytextclock = wibox.widget.textclock()
  338. calendar:register(mytextclock)
  339. -- Create a wibox for each screen and add it
  340. local taglist_buttons = awful.util.table.join(
  341. awful.button({ }, 1, function(t) t:view_only() end),
  342. awful.button({ modkey }, 1, function(t)
  343. if client.focus then
  344. client.focus:move_to_tag(t)
  345. end
  346. end),
  347. awful.button({ }, 3, awful.tag.viewtoggle),
  348. awful.button({ modkey }, 3, function(t)
  349. if client.focus then
  350. client.focus:toggle_tag(t)
  351. end
  352. end),
  353. awful.button({ }, 4, function(t) awful.tag.viewnext(t.screen) end),
  354. awful.button({ }, 5, function(t) awful.tag.viewprev(t.screen) end)
  355. )
  356. local tasklist_buttons = awful.util.table.join(
  357. awful.button({ }, 1, function (c)
  358. if c == client.focus then
  359. c.minimized = true
  360. else
  361. -- Without this, the following
  362. -- :isvisible() makes no sense
  363. c.minimized = false
  364. if not c:isvisible() and c.first_tag then
  365. c.first_tag:view_only()
  366. end
  367. -- This will also un-minimize
  368. -- the client, if needed
  369. client.focus = c
  370. c:raise()
  371. end
  372. end),
  373. awful.button({ }, 3, client_menu_toggle_fn()),
  374. awful.button({ }, 4, function ()
  375. awful.client.focus.byidx(1)
  376. end),
  377. awful.button({ }, 5, function ()
  378. awful.client.focus.byidx(-1)
  379. end))
  380. local function set_wallpaper(s)
  381. -- Wallpaper
  382. if beautiful.wallpaper then
  383. local wallpaper = beautiful.wallpaper
  384. -- If wallpaper is a function, call it with the screen
  385. if type(wallpaper) == "function" then
  386. wallpaper = wallpaper(s)
  387. end
  388. gears.wallpaper.maximized(wallpaper, s, true)
  389. end
  390. end
  391. -- Re-set wallpaper when a screen's geometry changes (e.g. different resolution)
  392. screen.connect_signal("property::geometry", set_wallpaper)
  393. local my_tags = { "A", "W", "E", "S", "O", "M", "E" }
  394. awful.screen.connect_for_each_screen(function(s)
  395. -- Wallpaper
  396. set_wallpaper(s)
  397. -- Each screen has its own tag table.
  398. -- https://awesomewm.org/recipes/awesome-taglist/
  399. awful.tag(my_tags, s, awful.layout.layouts[1])
  400. -- Create a promptbox for each screen
  401. s.mypromptbox = awful.widget.prompt()
  402. -- Create an imagebox widget which will contains an icon indicating which layout we're using.
  403. -- We need one layoutbox per screen.
  404. s.mylayoutbox = awful.widget.layoutbox(s)
  405. s.mylayoutbox:buttons(awful.util.table.join(
  406. awful.button({ }, 1, function () awful.layout.inc( 1) end),
  407. awful.button({ }, 3, function () awful.layout.inc(-1) end),
  408. awful.button({ }, 4, function () awful.layout.inc( 1) end),
  409. awful.button({ }, 5, function () awful.layout.inc(-1) end)))
  410. -- Create a taglist widget
  411. s.mytaglist = awful.widget.taglist({
  412. screen = s,
  413. filter = awful.widget.taglist.filter.all,
  414. buttons = taglist_buttons
  415. })
  416. -- and apply shape to it (needed by gtk theme)
  417. if beautiful.taglist_shape_container then
  418. local background_shape_wrapper = wibox.container.background(s.mytaglist)
  419. background_shape_wrapper._do_taglist_update_now = s.mytaglist._do_taglist_update_now
  420. background_shape_wrapper._do_taglist_update = s.mytaglist._do_taglist_update
  421. background_shape_wrapper.shape = beautiful.taglist_shape_container
  422. background_shape_wrapper.shape_clip = beautiful.taglist_shape_clip_container
  423. background_shape_wrapper.shape_border_width = beautiful.taglist_shape_border_width_container
  424. background_shape_wrapper.shape_border_color = beautiful.taglist_shape_border_color_container
  425. s.mytaglist = background_shape_wrapper
  426. end
  427. -- Create a tasklist widget
  428. s.mytasklist = awful.widget.tasklist({
  429. screen = s,
  430. filter = awful.widget.tasklist.filter.currenttags,
  431. buttons = tasklist_buttons,
  432. widget_template = beautiful.tasklist_widget_template
  433. })
  434. -- Create the wibox
  435. s.mywibox = awful.wibar({ position = "top", screen = s })
  436. -- Add widgets to the wibox
  437. s.mywibox:setup {
  438. layout = wibox.layout.align.horizontal,
  439. { -- Left widgets
  440. layout = wibox.layout.fixed.horizontal,
  441. s.mytaglist,
  442. s.mypromptbox,
  443. },
  444. s.mytasklist, -- Middle widget
  445. { -- Right widgets
  446. layout = wibox.layout.fixed.horizontal,
  447. wibox.widget.systray(),
  448. capslock,
  449. connman,
  450. power,
  451. mytextclock,
  452. pulse,
  453. s.mylayoutbox,
  454. },
  455. }
  456. end)
  457. -- }}}
  458. -- {{{ Mouse bindings
  459. root.buttons(awful.util.table.join(
  460. awful.button({ }, 4, awful.tag.viewnext),
  461. awful.button({ }, 5, awful.tag.viewprev)
  462. ))
  463. -- }}}
  464. -- {{{ Key bindings
  465. local globalkeys = awful.util.table.join(
  466. awful.key({ modkey, }, "s", hotkeys_popup.show_help,
  467. {description="show help", group="awesome"}),
  468. awful.key({ modkey, }, "Left", awful.tag.viewprev,
  469. {description = "view previous", group = "tag"}),
  470. awful.key({ modkey, }, "Right", awful.tag.viewnext,
  471. {description = "view next", group = "tag"}),
  472. awful.key({ modkey, }, "Escape", awful.tag.history.restore,
  473. {description = "go back", group = "tag"}),
  474. capslock.key,
  475. awful.key({ modkey, }, "j",
  476. function ()
  477. awful.client.focus.byidx( 1)
  478. end,
  479. {description = "focus next by index", group = "client"}
  480. ),
  481. awful.key({ modkey, }, "k",
  482. function ()
  483. awful.client.focus.byidx(-1)
  484. end,
  485. {description = "focus previous by index", group = "client"}
  486. ),
  487. -- Layout manipulation
  488. awful.key({ modkey, "Shift" }, "j", function () awful.client.swap.byidx( 1) end,
  489. {description = "swap with next client by index", group = "client"}),
  490. awful.key({ modkey, "Shift" }, "k", function () awful.client.swap.byidx( -1) end,
  491. {description = "swap with previous client by index", group = "client"}),
  492. awful.key({ modkey, "Control" }, "j", function () awful.screen.focus_relative( 1) end,
  493. {description = "focus the next screen", group = "screen"}),
  494. awful.key({ modkey, "Control" }, "k", function () awful.screen.focus_relative(-1) end,
  495. {description = "focus the previous screen", group = "screen"}),
  496. awful.key({ modkey, }, "u", awful.client.urgent.jumpto,
  497. {description = "jump to urgent client", group = "client"}),
  498. awful.key({ modkey, }, "Tab",
  499. function ()
  500. awful.spawn("rofi -show window")
  501. end,
  502. {description = "select windows", group = "client"}),
  503. -- Standard program
  504. awful.key({ modkey, }, "KP_Enter", function () awful.spawn(terminal) end,
  505. {description = "open a terminal", group = "launcher"}),
  506. awful.key({ modkey, }, "Return", function () awful.spawn(terminal) end,
  507. {description = "open a terminal", group = "launcher"}),
  508. awful.key({ modkey, "Control" }, "r", awesome.restart,
  509. {description = "reload awesome", group = "awesome"}),
  510. awful.key({ modkey, "Shift" }, "q",
  511. function ()
  512. naughty.notify(
  513. {title = "About to quit Awesome!",
  514. position = "top_left",
  515. timeout = 5,
  516. preset = naughty.config.presets.critical})
  517. awful.prompt.run(
  518. {prompt = "QUIT AWESOME [Y/N]? ",
  519. textbox = awful.screen.focused().mypromptbox.widget,
  520. exe_callback = function (arg)
  521. if arg:lower() == "y" then
  522. awesome.quit()
  523. end
  524. end
  525. }
  526. )
  527. end,
  528. {description = "quit awesome", group = "awesome"}),
  529. awful.key({ modkey, }, "l", function () awful.tag.incmwfact( 0.05) end,
  530. {description = "increase master width factor", group = "layout"}),
  531. awful.key({ modkey, }, "h", function () awful.tag.incmwfact(-0.05) end,
  532. {description = "decrease master width factor", group = "layout"}),
  533. awful.key({ modkey, "Shift" }, "h", function () awful.tag.incnmaster( 1, nil, true) end,
  534. {description = "increase the number of master clients", group = "layout"}),
  535. awful.key({ modkey, "Shift" }, "l", function () awful.tag.incnmaster(-1, nil, true) end,
  536. {description = "decrease the number of master clients", group = "layout"}),
  537. awful.key({ modkey, "Control" }, "h", function () awful.tag.incncol( 1, nil, true) end,
  538. {description = "increase the number of columns", group = "layout"}),
  539. awful.key({ modkey, "Control" }, "l", function () awful.tag.incncol(-1, nil, true) end,
  540. {description = "decrease the number of columns", group = "layout"}),
  541. awful.key({ modkey, "Control" }, "n",
  542. function ()
  543. local c = awful.client.restore()
  544. -- Focus restored client
  545. if c then
  546. client.focus = c
  547. c:raise()
  548. end
  549. end,
  550. {description = "restore minimized", group = "client"}),
  551. -- Prompt
  552. awful.key({ modkey }, "r", function () awful.screen.focused().mypromptbox:run() end,
  553. {description = "run prompt", group = "launcher"}),
  554. awful.key({ modkey }, "x",
  555. function ()
  556. awful.prompt.run {
  557. prompt = "Run Lua code: ",
  558. textbox = awful.screen.focused().mypromptbox.widget,
  559. exe_callback = awful.util.eval,
  560. history_path = awful.util.get_cache_dir() .. "/history_eval"
  561. }
  562. end,
  563. {description = "lua execute prompt", group = "awesome"}),
  564. -- Menubar
  565. awful.key({ modkey }, "p", function() awful.spawn("rofi -show drun") end,
  566. {description = "show the application menu", group = "launcher"}),
  567. -- Pulseaudio
  568. awful.key({ }, "XF86AudioRaiseVolume", pulse.volume_up),
  569. awful.key({ }, "XF86AudioLowerVolume", pulse.volume_down),
  570. awful.key({ }, "XF86AudioMute", pulse.toggle_muted),
  571. awful.key({"Shift"}, "XF86AudioRaiseVolume", pulse.volume_up_mic),
  572. awful.key({"Shift"}, "XF86AudioLowerVolume", pulse.volume_down_mic),
  573. awful.key({"Shift"}, "XF86AudioMute", pulse.toggle_muted_mic),
  574. awful.key({ }, "XF86AudioMicMute", pulse.toggle_muted_mic),
  575. -- QuodLibet music player
  576. awful.key({}, "XF86AudioPlay", quodlibet.play_pause),
  577. awful.key({}, "XF86AudioStop", quodlibet.stop),
  578. awful.key({"Control"}, "XF86AudioStop", quodlibet.quit),
  579. awful.key({}, "XF86AudioPrev", quodlibet.previous),
  580. awful.key({}, "XF86AudioNext", quodlibet.next),
  581. awful.key({modkey}, "i", quodlibet.notify),
  582. -- Vlc media player
  583. awful.key({"Shift"}, "XF86AudioPlay", vlc.play_pause),
  584. awful.key({"Shift"}, "XF86AudioStop", vlc.stop),
  585. awful.key({"Shift", "Control"}, "XF86AudioStop", vlc.quit),
  586. awful.key({"Shift"}, "XF86AudioPrev", vlc.previous),
  587. awful.key({"Shift"}, "XF86AudioNext", vlc.next),
  588. awful.key({modkey, "Shift"}, "i", vlc.notify),
  589. -- Xscreensaver: Ctrl+alt+l
  590. awful.key({"Control", "Mod1"}, "l", lock_screen,
  591. {description = "lock the screen", group = "screen"}),
  592. awful.key({}, "XF86ScreenSaver", lock_screen,
  593. {description = "lock the screen", group = "screen"}),
  594. -- Quake Terminal
  595. awful.key(
  596. {},
  597. "F12",
  598. function ()
  599. quake:spawn()
  600. end,
  601. {
  602. description = "toggle the drop-down terminal",
  603. group = "launcher"
  604. }
  605. )
  606. )
  607. local clientkeys = awful.util.table.join(
  608. awful.key({ modkey, }, "f",
  609. function (c)
  610. c.fullscreen = not c.fullscreen
  611. c:raise()
  612. end,
  613. {description = "toggle fullscreen", group = "client"}),
  614. awful.key({ modkey, "Shift" }, "c", function (c) c:kill() end,
  615. {description = "close", group = "client"}),
  616. awful.key({ modkey, "Control" }, "space", awful.client.floating.toggle ,
  617. {description = "toggle floating", group = "client"}),
  618. awful.key({ modkey, "Control" }, "KP_Enter", function (c) c:swap(awful.client.getmaster()) end,
  619. {description = "move to master", group = "client"}),
  620. awful.key({ modkey, "Control" }, "Return", function (c) c:swap(awful.client.getmaster()) end,
  621. {description = "move to master", group = "client"}),
  622. awful.key({ modkey, }, "o", function (c) c:move_to_screen() end,
  623. {description = "move to screen", group = "client"}),
  624. awful.key({ modkey, }, "t", function (c) c.ontop = not c.ontop end,
  625. {description = "toggle keep on top", group = "client"}),
  626. awful.key({ modkey, }, "n",
  627. function (c)
  628. -- The client currently has the input focus, so it cannot be
  629. -- minimized, since minimized clients can't have the focus.
  630. c.minimized = true
  631. end ,
  632. {description = "minimize", group = "client"}),
  633. awful.key({ modkey, }, "m",
  634. function (c)
  635. c.maximized = not c.maximized
  636. c:raise()
  637. end ,
  638. {description = "maximize", group = "client"}),
  639. awful.key({ modkey, }, "q", toggle_suspend,
  640. { description = "toggle suspend", group = "client" })
  641. )
  642. -- Bind all key numbers to tags.
  643. -- Be careful: we use keycodes to make it works on any keyboard layout.
  644. -- This should map on the top row of your keyboard.
  645. for i in ipairs(my_tags) do
  646. globalkeys = awful.util.table.join(globalkeys,
  647. -- View tag only.
  648. awful.key({ modkey }, "#" .. i + 9,
  649. function ()
  650. local scr = awful.screen.focused()
  651. local tag = scr.tags[i]
  652. if tag then
  653. tag:view_only()
  654. end
  655. end,
  656. {description = "view tag #"..i, group = "tag"}),
  657. -- Toggle tag display.
  658. awful.key({ modkey, "Control" }, "#" .. i + 9,
  659. function ()
  660. local scr = awful.screen.focused()
  661. local tag = scr.tags[i]
  662. if tag then
  663. awful.tag.viewtoggle(tag)
  664. end
  665. end,
  666. {description = "toggle tag #" .. i, group = "tag"}),
  667. -- Move client to tag.
  668. awful.key({ modkey, "Shift" }, "#" .. i + 9,
  669. function ()
  670. if client.focus then
  671. local tag = client.focus.screen.tags[i]
  672. if tag then
  673. client.focus:move_to_tag(tag)
  674. end
  675. end
  676. end,
  677. {description = "move focused client to tag #"..i, group = "tag"}),
  678. -- Toggle tag on focused client.
  679. awful.key({ modkey, "Control", "Shift" }, "#" .. i + 9,
  680. function ()
  681. if client.focus then
  682. local tag = client.focus.screen.tags[i]
  683. if tag then
  684. client.focus:toggle_tag(tag)
  685. end
  686. end
  687. end,
  688. {description = "toggle focused client on tag #" .. i, group = "tag"})
  689. )
  690. end
  691. local clientbuttons = awful.util.table.join(
  692. awful.button({ }, 1, function (c) client.focus = c; c:raise() end),
  693. awful.button({ modkey }, 1, awful.mouse.client.move),
  694. awful.button({ modkey }, 3, awful.mouse.client.resize))
  695. -- Set keys
  696. root.keys(globalkeys)
  697. -- }}}
  698. -- {{{ Rules
  699. -- Rules to apply to new clients (through the "manage" signal).
  700. awful.rules.rules = {
  701. -- All clients will match this rule.
  702. { rule = { },
  703. properties = { border_width = beautiful.border_width,
  704. border_color = beautiful.border_normal,
  705. focus = awful.client.focus.filter,
  706. raise = true,
  707. keys = clientkeys,
  708. buttons = clientbuttons,
  709. screen = awful.screen.preferred,
  710. placement = awful.placement.no_overlap + awful.placement.no_offscreen
  711. }
  712. },
  713. -- Floating clients.
  714. { rule_any = {
  715. instance = {
  716. "DTA", -- Firefox addon DownThemAll.
  717. "copyq", -- Includes session name in class.
  718. "pinentry",
  719. },
  720. class = {
  721. "1Password",
  722. "Arandr",
  723. "Gpick",
  724. "Kruler",
  725. "MessageWin", -- kalarm.
  726. "Sxiv",
  727. "Tor Browser", -- Needs a fixed window size to avoid fingerprinting by screen size.
  728. "Wpa_gui",
  729. "pinentry",
  730. "veromix",
  731. "xtightvncviewer",
  732. "MPlayer",
  733. "Vlc",
  734. "Pidgin",
  735. "Quodlibet",
  736. "Pinentry",
  737. "qemu-system-x86_64",
  738. },
  739. name = {
  740. "Event Tester", -- xev.
  741. "Task Manager - Chromium",
  742. },
  743. role = {
  744. "AlarmWindow", -- Thunderbird's calendar.
  745. "ConfigManager", -- Thunderbird's about:config.
  746. "pop-up", -- e.g. Google Chrome's (detached) Developer Tools.
  747. "About", -- e.g. "About Firefox"
  748. "page-info", -- Firefox page information.
  749. }
  750. }, properties = { floating = true }},
  751. -- Do not add titlebars to normal clients and dialogs
  752. { rule_any = {type = { "normal", "dialog" }
  753. }, properties = { titlebars_enabled = false }
  754. },
  755. }
  756. -- }}}
  757. -- {{{ Signals
  758. -- Signal function to execute when a new client appears.
  759. client.connect_signal("manage", function (c)
  760. -- Set the windows at the slave,
  761. -- i.e. put it at the end of others instead of setting it master.
  762. -- if not awesome.startup then awful.client.setslave(c) end
  763. if awesome.startup and
  764. not c.size_hints.user_position
  765. and not c.size_hints.program_position then
  766. -- Prevent clients from being unreachable after screen count changes.
  767. awful.placement.no_offscreen(c)
  768. end
  769. end)
  770. -- Enable sloppy focus, so that focus follows mouse.
  771. client.connect_signal("mouse::enter", function(c)
  772. c:emit_signal("request::activate", "mouse_enter", {raise = false})
  773. end)
  774. client.connect_signal("focus", function(c) c.border_color = beautiful.border_focus end)
  775. client.connect_signal("unfocus", function(c) c.border_color = beautiful.border_normal end)
  776. -- }}}