Skip to content

Instantly share code, notes, and snippets.

@YPetremann
Created November 11, 2021 01:33
Show Gist options
  • Save YPetremann/42c251af87f8e578d61727e063376696 to your computer and use it in GitHub Desktop.
Save YPetremann/42c251af87f8e578d61727e063376696 to your computer and use it in GitHub Desktop.
AwesomeWM easier binding

AwesomeWM easier key and mouse binding

Make it easier to define bindings by :

  • providing groups of bindings depending of context
  • provide a way to define every any bindings in one place (not mandatory through)
  • mixing key and mouse bindings (mouse bindings are detected as such and automaticaly moved to a separate set)
  • mouse bindings have more indicative alias (LMB, RMB, MWU, MWD)
  • easy text based binding definition
  • easy text based documentation definition
  • one time definition for multiple time usage (client, client_titlebar, taglists ...)
    • permit to pass parameters to bindings from specific context (client, tags, screens ...)
local awful = require("awful")
local gears = require("gears")
local hotkeys_popup = require("awful.hotkeys_popup").widget
local serpent = require("serpent")
local keygroups = {}
local alias ={
["LMB"]=1,
["MMB"]=2,
["RMB"]=3,
["MWU"]=4,
["MWD"]=5,
["Super"]="Mod4",
}
local name = {
"LMB",
"MMB",
"RMB",
"MWU",
"MWD",
}
function split(str, sep)
local sep, fields = sep or ":", {}
local pattern = string.format("([^%s]+)", sep)
str:gsub(pattern, function(c) fields[#fields+1] = c end)
return fields
end
function map(tbl, f) local t = {} for k,v in ipairs(tbl) do t[k] = f(v) end return t end
local function FixKey(bind) return alias[bind] or bind end
local function FixBinding(bind)
local obind = bind
if type(bind) == "string" then
bind = split(bind,"+")
end
bind = map(bind, FixKey)
return bind
end
local function FixDoc(doc)
if type(doc) == "string" then
local p1, p2 = doc:match("([^:]*): *(.*)")
doc = {group=p1, description=p2}
end
return doc
end
local function Button(mods, button, action, doc)
local buttonname = name[button] or button
if doc then
hotkeys_popup.add_hotkeys({
[doc.group] = {{
modifiers = mods,
keys = {
[buttonname] = doc.description
}
}}
})
end
return awful.button(mods, button, action)
end
function FixSet(set)
if set == nil then set = false end
if set == false then set = "key" end
if set == true then set = "button" end
return set
end
function GetGroup(group, set)
set = FixSet(set)
if not keygroups[group] then
keygroups[group] = {}
keygroups[group]["key"] = gears.table.join()
keygroups[group]["button"] = gears.table.join()
end
if not keygroups[group][set] then
keygroups[group]["key"][set] = gears.table.join()
end
return keygroups[group][set]
end
function SetGroup(group, bind, set)
set = FixSet(set)
local grp = GetGroup(group, set)
table.insert(grp, bind)
end
function BindToGroup(group, rbindings, action, doc)
local bindings = FixBinding(rbindings)
local documentation = FixDoc(doc)
local binding = table.remove(bindings)
local is_mouse = type(binding) == "number"
local bind = {bindings, binding, action, documentation}
SetGroup(group, bind, is_mouse)
return bind
end
local module = {}
--- Add key alias.
-- @param string source The source key (eg: "Super").
-- @param string target The target key (eg: "Mod4").
function module.alias(source, target)
alias[source] = target
end
--- Add a key binding to a group.
-- @param string group The group name to add binding (eg: "root").
-- @param string binding The key combination in string form (eg: "Mod4+Control+r").
-- @param string action The function to execute (eg: awesome.restart).
-- @param string documentation The documentation to display (eg: "Awesome: reload awesome").
function module.set(group, binding, action, documentation)
if type(group) ~= "string" then
documentation = action
action = binding
binding = group
group = "any"
end
return BindToGroup(group, binding, action, documentation)
end
--- Bind some variables to a function.
-- @param string func The function to use (eg: awful.client.floating.toggle).
-- @param any ... The parameters to bind to the function (eg: client).
-- @return The function with parameters bound.
function module.bind(func, ...)
local object = {...}
return function(...)
return func(table.unpack(object), ...)
end
end
--- Get a group of bindings.
-- @param string group The group to use (eg: "root").
-- @param string set The set to use between "key" and "button" (eg: "key").
-- @param any ... The parameters to bind to all functions of the set (eg: client).
-- @return The group of bindings.
function module.get(group, set, ...)
local group = GetGroup(group, set)
local new_group = gears.table.join()
for _, def in ipairs(group) do
local new_function = def[3]
if ... then
new_function = module.bind(def[3], ...)
end
local is_mouse = type(def[2]) == "number"
local bind = is_mouse
and Button(def[1], def[2], new_function, def[4])
or awful.key(def[1], def[2], new_function, def[4])
def[4] = nil
new_group = gears.table.join(new_group, bind)
end
return new_group
end
return module
local bind = require("modules.utils.bind")
local tags = require("modules.utils.tags")
local clients = require("modules.utils.clients")
local exec = require("modules.utils.exec")
local hotkeys_popup = require("awful.hotkeys_popup").widget
-- Main
bind.set("root", "Super+Control+r", awesome.restart, "Awesome: reload awesome")
bind.set("root", "Super+Control+q", awesome.quit, "Awesome: quit awesome")
bind.set("root", "Super+Return", exec(config.terminal, true), "Awesome: open a terminal")
bind.set("root", "Super+s", hotkeys_popup.show_help, "Awesome: show help")
-- bind.set("root", "Super+w", toggleMainMenu, "awesome: show main menu")
-- Screens
local screens = require("modules.utils.screens")
bind.set("root", "Super+Control+j", screens.focus_next_screen, "Screen: focus the next screen")
bind.set("root", "Super+Control+k", screens.focus_prev_screen, "Screen: focus the previous screen")
-- Layouts
local layouts = require("modules.utils.layouts")
bind.set("root", "Super+l", layouts.inc_master_width, "Layout: increase master width factor")
bind.set("root", "Super+h", layouts.dec_master_width, "Layout: decrease master width factor")
bind.set("root", "Super+Shift+h", layouts.inc_master_ammount, "Layout: increase the number of master clients")
bind.set("root", "Super+Shift+l", layouts.dec_master_ammount, "Layout: decrease the number of master clients")
bind.set("root", "Super+Control+h", layouts.inc_columns, "Layout: increase the number of columns")
bind.set("root", "Super+Control+l", layouts.dec_columns, "Layout: decrease the number of columns")
bind.set("root", "Super+space", layouts.next_layout, "Layouts: select next")
bind.set("root", "Super+Shift+space", layouts.prev_layout, "Layouts: select previous")
-- Clients
local clients = require("modules.utils.clients")
local awful = require("awful")
bind.set("root", "Super+Tab", clients.focus_last_client, "Clients: go back")
bind.set("root", "Super+j", clients.focus_next_client, "Clients: focus next client")
bind.set("root", "Super+k", clients.focus_prev_client, "Clients: focus prev client")
bind.set("root", "Super+Shift+j", clients.swap_prev_client, "Clients: swap with next client by index")
bind.set("root", "Super+Shift+k", clients.swap_next_client, "Clients: swap with previous client by index")
bind.set("root", "Super+u", awful.client.urgent.jumpto, "Clients: jump to urgent client")
bind.set("root", "Super+Control+n", clients.restore_minimized, "Client: restore minimized")
bind.set("client", "Mod4+f", clients.toggle_fullscreen, "Client: toggle fullscreen")
bind.set("client", "Mod4+Shift+c", clients.kill, "Client: close")
bind.set("client", "Mod4+Control+space", awful.client.floating.toggle, "Client: toggle floating")
bind.set("client", "Mod4+Control+Return", clients.move_to_master, "Client: move to master")
bind.set("client", "Mod4+o", clients.move_to_screen, "Client: move to screen")
bind.set("client", "Super+t", clients.toggle_ontop, "Client: toggle keep on top")
bind.set("client", "Mod4+n", clients.toggle_minimize, "Client: minimize")
bind.set("client", "Mod4+m", clients.toggle_maximize, "Client: (un)maximize")
bind.set("client", "Super+Control+m", clients.toggle_maximize_vertical, "Client: (un)maximize vertically")
bind.set("client", "Mod4+Shift+m", clients.toggle_maximize_horizontal, "Client: (un)maximize horizontally")
bind.set("client", "LMB", clients.raise_through, "Client: raise client")
bind.set("client", "Super+LMB", clients.mouse_move_client, "Client: move client")
bind.set("client", "Super+RMB", clients.mouse_resize_client, "Client: resize client")
bind.set("client_icon", "LMB", clients.raise_through)
bind.set("client_icon", "RMB", clients.client_menu, "Client: (on titlebar icon) client menu")
bind.set("client_titlebar", "LMB", clients.mouse_move_client, "Client: (on titlebar) move client")
bind.set("client_titlebar", "RMB", clients.mouse_resize_client, "Client: (on titlebar) resize client")
-- Machi
local machi = require("modules.layout-machi")
bind.set("root", "Super+;", machi.default_editor.start_interactive, "Layout: machi interactive configuration")
bind.set("root", "Super+:", function() machi.switcher.start(client.focus) end, "Layout: machi interactive clients management")
-- Tags
local awful = require("awful")
bind.set("root", "MWU", awful.tag.viewnext, "Tag: next tag")
bind.set("root", "MWD", awful.tag.viewprev, "Tag: previous tag ")
bind.set("root", "Super+Left", awful.tag.viewprev, "Tag: view previous")
bind.set("root", "Super+Right", awful.tag.viewnext, "Tag: view next")
bind.set("root", "Super+Escape", awful.tag.history.restore, "Tag: go back")
for i = 1, 9 do
bind.set("root", "Super+#"..i+9, bind.bind(tags.view_tag, i), "Tag: view tag #"..i)
bind.set("root", "Super+Control+#"..i+9, bind.bind(tags.toggle_tag, i), "Tag: toggle tag #"..i)
bind.set("root", "Super+Shift+#"..i+9, bind.bind(tags.move_focused_client_to, i), "Tag: move focused client to tag #"..i)
bind.set("root", "Super+Control+Shift+#"..i+9, bind.bind(tags.toggle_focused_client_on, i), "Tag: toggle focused client on tag #"..i)
end
root.keys(bind.get("root", "key"))
root.buttons(bind.get("root", "button"))
client.connect_signal("request::titlebars", function(c)
awful.titlebar(c, { size = beautiful.get().menu_height }):setup{
{
awful.titlebar.widget.iconwidget(c),
buttons = bind.get("client_icon", "button", c),
layout = wibox.layout.fixed.horizontal,
},
{
{
align = "center",
widget = awful.titlebar.widget.titlewidget(c)
},
buttons = bind.get("client_titlebar", "button", c),
layout = wibox.layout.flex.horizontal,
},
{
awful.titlebar.widget.closebutton(c),
layout = wibox.layout.fixed.horizontal(),
},
size = 32,
layout = wibox.layout.align.horizontal,
}
end)
@Syakhisk
Copy link

Great work!

@mkrusemark
Copy link

mkrusemark commented Nov 16, 2021

I like it! Could you also share your modules, e.g. tags, clients, screens, ...?

@YPetremann
Copy link
Author

these are mostly functions to prevent function definition inside the keymap file. For example :

layouts.prev_layout = function() awful.layout.inc(-1) end
layouts.next_layout = function() awful.layout.inc(1) end
clients.toggle_ontop = function clients(c) c.ontop = not c.ontop end

the idea is I've moved them in modules coresponding to their use and simple used them
feel free to make some that you want and if their is cases you specificly want and don't know how to make them, ask then, I will make a separate gist for a collection of thoses functions

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment