Lua-configured input daemon for X
lucky
is an input daemon for Xorg configured with a lua script.
To get a quick overview of what can be done, see the examples.
Note: requires Zig master, you can download builds at https://ziglang.org/download
Dependencies:
xcb
xcb-keysyms
To install, run zig build -p <prefix>
where <prefix>
is the directory that contains the bin
folder you want it to install to.
So to install it to the system you could do:
sudo zig build -p /usr/local
and to install it for just the current user you could do:
zig build -p ~/.local
You may also define a different build mode using -Doptimize=<mode>
, the default is ReleaseFast
press
and release
functions to keys and mouse buttonsmotion
and window enter
and exit
functions to mouse buttons
motion_resolution
option to determine how often to read motion events. Defaults to 5 so motion binds don't overwhelm your system (i.e. every 5th motion event will call the binding's motion
callback).-c
, --config
- file path to load for the config, defaults to XDG_CONFIG_HOME/lucky/config.lua
lucky.bind(bind_string, { ... })
- bind a modifier+key/mouse button combinationlucky.cmd(command, arg1, arg2, ...)
- run a command directly, with function argument being a new argument to the commandlucky.shell(command_string)
- runs command_string
under the system's shell, useful if you want to use subshells, pipelines, file redirects, etc. which are not available when running a command through cmd
lucky.is_root(window_id)
- returns true if window_id
is the root windowlucky.get_root()
- returns the ID of the root window (based on the currently focused window)lucky.get_parent_window(window_id)
- return the ID of the immediate non-root parent of window_id
, if no parent exist return window_id
lucky.get_top_level_window(window_id)
- return the ID of the top level non-root parent of window_id
, if no parents exist return window_id
lucky.get_focused_window()
- return the ID of the currently focused windowlucky.get_geometry(window_id)
- returns a table containing the x
, y
, width
, height
, and border_width
of window_id
lucky.get_title(window_id)
- returns the title of window_id
lucky.get_class(window_id)
- returns the class of window_id
lucky.get_instance(window_id)
- returns the instance of window_id
lucky.reload()
- reload your configlucky.bind('super Return', {
press = function(window_id)
lucky.cmd(os.getenv('TERMINAL') or 'xterm')
end
})
for i=1,9 do
lucky.bind('super ' .. tostring(i), {
press = function()
lucky.cmd('dwmc', 'viewex', tostring(i - 1))
end,
})
end
-- when Discord is focused, this keybinding will be active
lucky.bind('alt Return', {
filter = function(wid)
return lucky.get_class(wid) == "discord"
end,
press = function()
lucky.cmd('notify-send', 'Discord is the current focused window')
end
})
-- when Discord is not focused, this keybinding will be active
lucky.bind('alt Return', {
press = function()
lucky.cmd('notify-send', 'Discord is NOT the current focused window')
end
})
lucky.bind('super minus', {
press = function(window_id)
lucky.reload()
end
})
lucky.bind('super p', {
press = function(window_id)
lucky.shell('mpv --force-window=immediate "$(xclip -o -sel clip)"')
end
})
super alt mouse_left
function pointer_to_tag(x, y, window_id)
local max_x = lucky.get_geometry(lucky.get_root()).width - 1
local percent = x / max_x
local tag = math.floor(percent * 9) -- 9 tags
lucky.cmd('dwmc', 'viewex', tostring(tag))
end
lucky.bind("super alt mouse_left", {
press = pointer_to_tag,
motion = pointer_to_tag
})
function volume_slider(x, y, wid)
local max_y = lucky.get_geometry(lucky.get_root()).height - 1
local percent = math.floor(((max_y - y) / max_y) * 100)
lucky.cmd('pactl', 'set-sink-volume', '@DEFAULT_SINK@', tostring(percent) .. '%')
end
lucky.bind("mouse_left", {
filter = function(x, y, wid)
return x == 0
end,
press = volume_slider,
motion = volume_slider
})