├── README.md └── lib └── dmenu.lua /README.md: -------------------------------------------------------------------------------- 1 | 2 | Native dmenu for the Awesome window manager 3 | =========================================== 4 | 5 | The dmenu tool that comes with Linux is great, but using in within the Awesome window manager is cumbersome. 6 | This is where _dmenu.lua_ comes handy. It does 99% of what dmenu does, but it plays well with Awesome's rc.lua. In other words, you can select an item from a predefined list of items, then spawn a process or run a callback function. 7 | 8 | Usage 9 | ----- 10 | 11 | In rc.lua: 12 | 13 | local dmenu = require("lib/dmenu") 14 | 15 | ... 16 | 17 | mydmenu = dmenu({ 18 | chromium = "chromium", 19 | vifm = "vifm", 20 | vim = terminal .. " -e vim", 21 | urxvt = function() 22 | local matcher = function (c) 23 | return awful.rules.match(c, {class = 'URxvt'}) 24 | end 25 | awful.client.run_or_raise(exec, matcher) 26 | end 27 | }) 28 | 29 | ... 30 | 31 | mywibox:set_widget(mydmenu.textbox) 32 | 33 | ... 34 | 35 | -- Execute 36 | awful.key({ modkey }, "r", function () 37 | mydmenu:show() 38 | end) 39 | 40 | Keys 41 | ---- 42 | 43 | dmenu.lua handles the same keyboard shortcuts as the Linux dmenu. Once activated, it will show a list of the table keys. In the above example that would be _chromium | vifm | vim | urxvt_. 44 | Left and right keys will move the selection. Typing will reduce the list to the items matching the typed string. Return will execute and Escape will exit. 45 | 46 | TODO 47 | ---- 48 | 49 | * If the list of items is too long, it gets cut off and navigation stops at the last visible item (as opposed to shifting the items by one and showing the next one). 50 | * A custom key handler could be installed in case someone requires more complicated keyboard shortcuts. 51 | 52 | -------------------------------------------------------------------------------- /lib/dmenu.lua: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------- 2 | -- Native dmenu for the Awesome window manager. 3 | -- Written by Stefan G. (naturalist@github) 4 | -- License: BSD 5 | --------------------------------------------------------------------------- 6 | 7 | local awful = require("awful") 8 | local textbox = require("wibox.widget.textbox") 9 | local keygrabber = require("awful.keygrabber") 10 | local beautiful = require("beautiful") 11 | 12 | local index = 0 13 | local typed = "" 14 | 15 | local _dmenu = { 16 | nor_bg_color = beautiful.bg_normal or "black", 17 | nor_fg_color = beautiful.fg_normal or "white", 18 | sel_bg_color = beautiful.bg_focus or "white", 19 | sel_fg_color = beautiful.fg_focus or "black", 20 | separator = " | ", 21 | prompt = "Run:" 22 | } 23 | local dmenu = {} 24 | local mt = {} 25 | setmetatable(dmenu, mt) 26 | 27 | local function get_names(str) 28 | local names = {} 29 | for k, _ in pairs(dmenu.items) do 30 | if str == nil or str == "" then 31 | table.insert(names, k) 32 | else 33 | if string.find(k, str) then 34 | table.insert(names, k) 35 | end 36 | end 37 | end 38 | table.sort(names) 39 | return names 40 | end 41 | 42 | local function draw() 43 | local formatted = {} 44 | for i, k in ipairs(dmenu.names) do 45 | local val = k 46 | if index % #dmenu.names + 1 == i then 47 | val = "" .. 49 | k .. "" 50 | end 51 | table.insert(formatted, val) 52 | end 53 | 54 | local prompt = string.format( 55 | '%s', 56 | dmenu.sel_fg_color, dmenu.sel_bg_color, dmenu.prompt 57 | ) 58 | 59 | local items = string.format( 60 | '%s', 61 | dmenu.nor_fg_color, dmenu.nor_bg_color, 62 | table.concat(formatted, dmenu.separator) 63 | ) 64 | 65 | local markup = string.format( 66 | '%s (%s) %s', 67 | prompt, typed, items 68 | ) 69 | 70 | dmenu.textbox:set_markup( markup ) 71 | end 72 | 73 | function dmenu.new(items, args) 74 | local args = args or {} 75 | for k, _ in pairs(_dmenu) do 76 | if args[k] then _dmenu[k] = args[k] end 77 | end 78 | dmenu.textbox = textbox() 79 | if args.font then dmenu.textbox:set_font(args.font) end 80 | dmenu.items = items 81 | return dmenu 82 | end 83 | 84 | function dmenu:show() 85 | dmenu.names = get_names(typed) 86 | local grabber 87 | grabber = keygrabber.run(function(mod, key, event) 88 | if event == "release" then return end 89 | 90 | local _typed = typed 91 | 92 | if key == "Right" then 93 | index = index + 1 94 | elseif key == "Left" then 95 | index = index - 1 96 | elseif #key == 1 and (string.lower(key) >= "a" or string.lower(key) <= "z") then 97 | typed = typed .. key 98 | elseif key == "BackSpace" then 99 | if typed ~= "" then 100 | typed = string.sub(typed, 1, #typed - 1) 101 | end 102 | elseif key == "Return" or key == "Escape" then 103 | if key == "Return" then 104 | local callback = dmenu.items[self.names[index + 1]] 105 | if type(callback) == "function" then 106 | callback() 107 | elseif type(callback) == "string" then 108 | awful.util.spawn(callback) 109 | end 110 | end 111 | keygrabber.stop(grabber) 112 | self:hide() 113 | return 114 | end 115 | 116 | if typed ~= _typed then 117 | dmenu.names = get_names(typed) 118 | end 119 | 120 | draw() 121 | end) 122 | end 123 | 124 | function dmenu:hide() 125 | self.textbox:set_markup("") 126 | index = 0 127 | typed = "" 128 | end 129 | 130 | function mt:__call(...) 131 | return dmenu.new(...) 132 | end 133 | 134 | function mt.__index(t, k) 135 | return _dmenu[k] 136 | end 137 | 138 | function mt.__newindex(t, k, v) 139 | _dmenu[k] = v 140 | if k == "names" then 141 | index = 0 142 | draw() 143 | end 144 | end 145 | 146 | return dmenu 147 | --------------------------------------------------------------------------------