diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4634daa71..7e81aee8e 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -22,10 +22,7 @@ include_directories(
${AWESOME_REQUIRED_INCLUDE_DIRS}
${AWESOME_OPTIONAL_INCLUDE_DIRS})
-set(AWE_LUA_FILES
- ${BUILD_DIR}/lib/tabulous.lua
- ${BUILD_DIR}/lib/beautiful.lua
- ${BUILD_DIR}/lib/awful.lua)
+file(GLOB_RECURSE AWE_LUA_FILES ${BUILD_DIR}/lib/*.lua)
set(AWE_CONF_FILE_DEFAULT ${BUILD_DIR}/awesomerc.lua)
set(AWE_CONF_FILE rc.lua)
@@ -294,9 +291,10 @@ if(GENERATE_LUADOC)
VERBATIM)
# }}}
- # dont include full path names in documentation
- foreach(filename ${AWE_LUA_FILES} ${capi_lua})
- get_filename_component(filename ${filename} NAME)
+ # Generate documentation of lib//0lua
+ file(GLOB_RECURSE lua_lib_files ${BUILD_DIR}/lib/*.lua)
+ foreach(filename ${lua_lib_files})
+ file(RELATIVE_PATH filename ${BUILD_DIR}/lib ${filename})
set(luadoc_srcs ${luadoc_srcs} ${filename})
endforeach()
@@ -318,7 +316,7 @@ endif()
# {{{ Installation
install(TARGETS ${PROJECT_AWE_NAME} ${PROJECT_AWECLIENT_NAME} RUNTIME DESTINATION bin)
install(FILES "utils/awsetbg" DESTINATION bin PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE)
-install(FILES ${AWE_LUA_FILES} DESTINATION ${AWESOME_LUA_LIB_PATH})
+install(DIRECTORY ${BUILD_DIR}/lib DESTINATION ${AWESOME_DATA_PATH})
install(FILES ${AWE_CONF_FILE_DEFAULT} DESTINATION ${AWESOME_SYSCONFDIR}
RENAME ${AWE_CONF_FILE})
if(GENERATE_MANPAGES)
diff --git a/awesomeConfig.cmake b/awesomeConfig.cmake
index 6dd7a8916..6bafa1286 100644
--- a/awesomeConfig.cmake
+++ b/awesomeConfig.cmake
@@ -266,13 +266,12 @@ set(AWESOME_THEMES_PATH ${AWESOME_DATA_PATH}/themes)
# }}}
# {{{ Configure files
-set(AWESOME_CONFIGURE_FILES
+file(GLOB_RECURSE awesome_lua_configure_files RELATIVE ${SOURCE_DIR} ${SOURCE_DIR}/lib/*.lua.in)
+set(AWESOME_CONFIGURE_FILES
+ ${awesome_lua_configure_files}
config.h.in
awesomerc.lua.in
themes/default.in
- lib/awful.lua.in
- lib/beautiful.lua.in
- lib/tabulous.lua.in
awesome-version-internal.h.in
awesome.doxygen.in)
diff --git a/awesomerc.lua.in b/awesomerc.lua.in
index d4643f9b3..af9d3b816 100644
--- a/awesomerc.lua.in
+++ b/awesomerc.lua.in
@@ -108,8 +108,8 @@ mytaglist.label = awful.widget.taglist.label.all
mytasklist = widget({ type = "tasklist", name = "mytasklist" })
mytasklist:buttons({
button({ }, 1, function (object, c) client.focus = c; c:raise() end),
- button({ }, 4, function () awful.client.focusbyidx(1) end),
- button({ }, 5, function () awful.client.focusbyidx(-1) end)
+ button({ }, 4, function () awful.client.focus.byidx(1) end),
+ button({ }, 5, function () awful.client.focus.byidx(-1) end)
})
mytasklist.label = awful.widget.tasklist.label.currenttags
@@ -162,7 +162,7 @@ end
-- {{{ Mouse bindings
awesome.buttons({
- button({ }, 3, function () awful.spawn(terminal) end),
+ button({ }, 3, function () awful.util.spawn(terminal) end),
button({ }, 4, awful.tag.viewnext),
button({ }, 5, awful.tag.viewprev)
})
@@ -215,7 +215,7 @@ keybinding({ modkey }, "Right", awful.tag.viewnext):add()
keybinding({ modkey }, "Escape", awful.tag.history.restore):add()
-- Standard program
-keybinding({ modkey }, "Return", function () awful.spawn(terminal) end):add()
+keybinding({ modkey }, "Return", function () awful.util.spawn(terminal) end):add()
keybinding({ modkey, "Control" }, "r", awesome.restart):add()
keybinding({ modkey, "Shift" }, "q", awesome.quit):add()
@@ -224,8 +224,8 @@ keybinding({ modkey, "Shift" }, "q", awesome.quit):add()
keybinding({ modkey }, "m", awful.client.maximize):add()
keybinding({ modkey }, "f", function () client.focus.fullscreen = not client.focus.fullscreen end):add()
keybinding({ modkey, "Shift" }, "c", function () client.focus:kill() end):add()
-keybinding({ modkey }, "j", function () awful.client.focusbyidx(1); client.focus:raise() end):add()
-keybinding({ modkey }, "k", function () awful.client.focusbyidx(-1); client.focus:raise() end):add()
+keybinding({ modkey }, "j", function () awful.client.focus.byidx(1); client.focus:raise() end):add()
+keybinding({ modkey }, "k", function () awful.client.focus.byidx(-1); client.focus:raise() end):add()
keybinding({ modkey, "Shift" }, "j", function () awful.client.swap(1) end):add()
keybinding({ modkey, "Shift" }, "k", function () awful.client.swap(-1) end):add()
keybinding({ modkey, "Control" }, "j", function () awful.screen.focus(1) end):add()
@@ -249,7 +249,7 @@ keybinding({ modkey, "Shift" }, "space", function () awful.layout.inc(layouts, -
-- Prompt
keybinding({ modkey }, "F1", function ()
- awful.prompt.run({ prompt = "Run: " }, mypromptbox, awful.spawn, awful.completion.bash,
+ awful.prompt.run({ prompt = "Run: " }, mypromptbox, awful.util.spawn, awful.completion.bash,
os.getenv("HOME") .. "/.cache/awesome/history") end):add()
keybinding({ modkey }, "F4", function ()
awful.prompt.run({ prompt = "Run Lua code: " }, mypromptbox, awful.eval, awful.prompt.bash,
diff --git a/lib/awful.lua.in b/lib/awful.lua.in
deleted file mode 100644
index 9b4e76795..000000000
--- a/lib/awful.lua.in
+++ /dev/null
@@ -1,1858 +0,0 @@
----------------------------------------------------------------------------
--- @author Julien Danjou <julien@danjou.info>
--- @copyright 2008 Julien Danjou
--- @release @AWESOME_VERSION@
----------------------------------------------------------------------------
-
--- Grab environment we need
-local type = type
-local string = string
-local assert = assert
-local loadstring = loadstring
-local ipairs = ipairs
-local pairs = pairs
-local os = os
-local io = io
-local math = math
-local setmetatable = setmetatable
-local table = table
-local otable = otable
-local capi =
-{
- awesome = awesome,
- screen = screen,
- client = client,
- mouse = mouse,
- button = button,
- wibox = wibox,
- widget = widget,
- hooks = hooks,
- keygrabber = keygrabber
-}
-
---- awful: AWesome Functions very UsefuL
-module("awful")
-
--- Local variable handling theme
-local theme = {}
-
--- mapping of command/completion function
-local bashcomp_funcs = {}
-local bashcomp_src = "/etc/bash_completion"
-
--- Various public structures
-beautiful = {}
-hooks = {}
-hooks.user = {}
-prompt = {}
-prompt.history = {}
-prompt.history.data = {}
-completion = {}
-screen = {}
-layout = {}
-client = {}
-client.data = {}
-client.data.maximize = otable()
-client.focus = {}
-client.focus.history = {}
-client.focus.history.data = {}
-tag = {}
-tag.history = {}
-tag.history.data = {}
-tag.history.data.past = {}
-tag.history.data.current = {}
-titlebar = {}
-titlebar.data = otable()
-widget = {}
-widget.taglist = {}
-widget.taglist.label = {}
-widget.tasklist = {}
-widget.tasklist.label = {}
-client.urgent = {}
-client.urgent.stack = {}
-client.urgent.stack.data = {}
-placement = {}
-
---- Strip alpha part of color.
--- @param color The color.
--- @return The color without alpha channel.
-local function color_strip_alpha(color)
- if color:len() == 9 then
- color = color:sub(1, 7)
- end
- return color
-end
-
---- Make i cycle.
--- @param t A length.
--- @param i An absolute index to fit into #t.
--- @return The object at new index.
-local function cycle(t, i)
- while i > t do i = i - t end
- while i < 1 do i = i + t end
- return i
-end
-
---- Create a directory
--- @param dir The directory.
--- @return mkdir return code
-function mkdir(dir)
- return os.execute("mkdir -p " .. dir)
-end
-
---- Get the first client that got the urgent hint.
--- @return The first urgent client.
-function client.urgent.get()
- if #client.urgent.stack.data > 0 then
- return client.urgent.stack.data[1]
- else
- -- fallback behaviour: iterate through clients and get the first urgent
- local clients = capi.client.get()
- for k, cl in pairs(clients) do
- if cl.urgent then
- return cl
- end
- end
- end
-end
-
---- Jump to the client that received the urgent hint first.
-function client.urgent.jumpto()
- local c = client.urgent.get()
- if c then
- local s = capi.client.focus and capi.client.focus.screen or capi.mouse.screen
- -- focus the screen
- if s ~= c.screen then
- capi.mouse.screen = c.screen
- end
- -- focus the tag
- tag.viewonly(c:tags()[1])
- -- focus the client
- capi.client.focus = c
- c:raise()
- end
-end
-
---- Adds client to urgent stack.
--- @param c The client object.
--- @param prop The property which is updated.
-function client.urgent.stack.add(c, prop)
- if prop == "urgent" then
- table.insert(client.urgent.stack.data, c)
- end
-end
-
---- Remove client from urgent stack.
--- @param c The client object.
-function client.urgent.stack.delete(c)
- for k, cl in ipairs(client.urgent.stack.data) do
- if c == cl then
- table.remove(client.urgent.stack.data, k)
- break
- end
- end
-end
-
---- Remove a client from the focus history
--- @param c The client that must be removed.
-function client.focus.history.delete(c)
- for k, v in ipairs(client.focus.history.data) do
- if v == c then
- table.remove(client.focus.history.data, k)
- break
- end
- end
-end
-
---- Filter out window that we do not want handled by focus.
--- This usually means that desktop, dock and splash windows are
--- not registered and cannot get focus.
--- @param c A client.
--- @return The same client if it's ok, nil otherwise.
-function client.focus.filter(c)
- if c.type == "desktop"
- or c.type == "dock"
- or c.type == "splash" then
- return nil
- end
- return c
-end
-
---- Update client focus history.
--- @param c The client that has been focused.
-function client.focus.history.add(c)
- if client.focus.filter(c) then
- -- Remove the client if its in stack
- client.focus.history.delete(c)
- -- Record the client has latest focused
- table.insert(client.focus.history.data, 1, c)
- end
-end
-
---- Get the latest focused client for a screen in history.
--- @param screen The screen number to look for.
--- @param idx The index: 0 will return first candidate,
--- 1 will return second, etc.
--- @return A client.
-function client.focus.history.get(screen, idx)
- -- When this counter is equal to idx, we return the client
- local counter = 0
- local vc = client.visible(screen)
- for k, c in ipairs(client.focus.history.data) do
- if c.screen == screen then
- for j, vcc in ipairs(vc) do
- if vcc == c then
- if counter == idx then
- return c
- end
- -- We found one, increment the counter only.
- counter = counter + 1
- break
- end
- end
- end
- end
- -- Argh nobody found in history, give the first one visible if there is one
- if counter == 0 then
- return vc[1]
- end
-end
-
---- Focus the previous client in history.
-function client.focus.history.previous()
- local sel = capi.client.focus
- local s
- if sel then
- s = sel.screen
- else
- s = capi.mouse.screen
- end
- local c = client.focus.history.get(s, 1)
- if c then capi.client.focus = c end
-end
-
---- Get visible clients from a screen.
--- @param The screen number, or nil for all screen.
--- @return A table with all visible clients.
-function client.visible(screen)
- local cls = capi.client.get(screen)
- local vcls = {}
- for k, c in pairs(cls) do
- if c:isvisible() then
- table.insert(vcls, c)
- end
- end
- return vcls
-end
-
---- Get a client by its relative index to the focused window.
--- @usage Set i to 1 to get next, -1 to get previous.
--- @param i The index.
--- @param c Optional client.
--- @return A client, or nil if no client is available.
-function client.next(i, c)
- -- Get currently focused client
- local sel = c or capi.client.focus
- if sel then
- -- Get all visible clients
- local cls = client.visible(sel.screen)
- -- Remove all no-normal clients
- for idx, c in ipairs(cls) do
- if not client.focus.filter(c) then
- table.remove(cls, idx)
- end
- end
- -- Loop upon each client
- for idx, c in ipairs(cls) do
- if c == sel then
- -- Cycle
- return cls[cycle(#cls, idx + i)]
- end
- end
- end
-end
-
---- Return true whether client B is in the right direction
--- compared to client A.
--- @param dir The direction.
--- @param cA The first client.
--- @param cB The second client.
--- @return True if B is in the direction of A.
-local function is_in_direction(dir, cA, cB)
- if dir == "up" then
- return cA['y'] > cB['y']
- elseif dir == "down" then
- return cA['y'] < cB['y']
- elseif dir == "left" then
- return cA['x'] > cB['x']
- elseif dir == "right" then
- return cA['x'] < cB['x']
- end
- return false
-end
-
---- Calculate distance between two points.
--- i.e: if we want to move to the right, we will take the right border
--- of the currently focused client and the left side of the checked client.
--- This avoid the focus of an upper client when you move to the right in a
--- tilebottom layout with nmaster=2 and 5 clients open, for instance.
--- @param dir The direction.
--- @param cA The first client.
--- @param cB The second client.
--- @return The distance between the clients.
-local function calculate_distance(dir, cA, cB)
- local xA = cA['x']
- local xB = cB['x']
- local yA = cA['y']
- local yB = cB['y']
-
- if dir == "up" then
- yB = yB + cB['height']
- elseif dir == "down" then
- yA = yA + cA['height']
- elseif dir == "left" then
- xB = xB + cB['width']
- elseif dir == "right" then
- xA = xA + cA['width']
- end
-
- return math.sqrt(math.pow(xB - xA, 2) + math.pow(yB - yA, 2))
-end
-
---- Focus a client by the given direction.
--- @param dir The direction, can be either "up", "down", "left" or "right".
--- @param c Optional client.
-function client.focusbydirection(dir, c)
- local sel = c or capi.client.focus
- if sel then
- local coords = sel:coords()
- local dist, dist_min
- local target = nil
- local cls = client.visible(sel.screen)
-
- -- We check each client.
- for i, c in ipairs(cls) do
- -- Check coords to see if client is located in the right direction.
- if is_in_direction(dir, coords, c:coords()) then
-
- -- Calculate distance between focused client and checked client.
- dist = calculate_distance(dir, coords, c:coords())
-
- -- If distance is shorter then keep the client.
- if not target or dist < dist_min then
- target = c
- dist_min = dist
- end
- end
- end
-
- -- If we found a client to focus, then do it.
- if target then
- capi.client.focus = target
- end
- end
-end
-
---- Focus a client by its relative index.
--- @param i The index.
--- @param c Optional client.
-function client.focusbyidx(i, c)
- local target = client.next(i, c)
- if target then
- capi.client.focus = target
- end
-end
-
---- Swap a client by its relative index.
--- @param i The index.
--- @param c Optional client, otherwise focused one is used.
-function client.swap(i, c)
- local sel = c or capi.client.focus
- local target = client.next(i, sel)
- if target then
- target:swap(sel)
- end
-end
-
---- Get the master window.
--- @param screen Optional screen number, otherwise screen mouse is used.
--- @return The master window.
-function client.master(screen)
- local s = screen or capi.mouse.screen
- return client.visible(s)[1]
-end
-
--- Set the client as slave: put it at the end of other windows.
--- @param c The window to set as slave.
--- @return
-function client.setslave(c)
- local cls = client.visible(c.screen)
- for k, v in pairs(cls) do
- c:swap(v)
- end
-end
-
---- Move/resize a client relative to current coordinates.
--- @param x The relative x coordinate.
--- @param y The relative y coordinate.
--- @param w The relative width.
--- @param h The relative height.
--- @param c The optional client, otherwise focused one is used.
-function client.moveresize(x, y, w, h, c)
- local sel = c or capi.client.focus
- local coords = sel:coords()
- coords['x'] = coords['x'] + x
- coords['y'] = coords['y'] + y
- coords['width'] = coords['width'] + w
- coords['height'] = coords['height'] + h
- sel:coords(coords)
-end
-
---- Maximize a client to use the full workarea.
--- @param c A client, or the focused one if nil.
-function client.maximize(c)
- local sel = c or capi.client.focus
- if sel then
- local ws = capi.screen[sel.screen].workarea
- ws.width = ws.width - 2 * sel.border_width
- ws.height = ws.height - 2 * sel.border_width
- if sel.floating and client.data.maximize[sel] then
- sel.floating = client.data.maximize[sel].floating
- if sel.floating then
- sel:coords(client.data.maximize[sel].coords)
- end
- client.data.maximize[sel] = nil
- else
- client.data.maximize[sel] = { coords = sel:coords(), floating = sel.floating }
- sel.floating = true
- sel:coords(ws)
- end
- end
-end
-
-
---- Erase eventual client data in maximize.
--- @param c The client.
-local function client_maximize_clean(c)
- client.data.maximize[c] = nil
-end
-
---- Give the focus to a screen, and move pointer.
--- @param Screen number.
-function screen.focus(i)
- local s = cycle(capi.screen.count(), capi.mouse.screen + i)
- local c = client.focus.history.get(s, 0)
- if c then capi.client.focus = c end
- -- Move the mouse on the screen
- capi.mouse.screen = s
-end
-
---- Compare 2 tables of tags.
--- @param a The first table.
--- @param b The second table of tags.
--- @return True if the tables are identical, false otherwise.
-local function tag_compare_select(a, b)
- if not a or not b then
- return false
- end
- -- Quick size comparison
- if #a ~= #b then
- return false
- end
- for ka, va in pairs(a) do
- if b[ka] ~= va.selected then
- return false
- end
- end
- for kb, vb in pairs(b) do
- if a[kb].selected ~= vb then
- return false
- end
- end
- return true
-end
-
---- Update the tag history.
--- @param screen The screen number.
-function tag.history.update(screen)
- local curtags = capi.screen[screen]:tags()
- if not tag_compare_select(curtags, tag.history.data.current[screen]) then
- tag.history.data.past[screen] = tag.history.data.current[screen]
- tag.history.data.current[screen] = {}
- for k, v in ipairs(curtags) do
- tag.history.data.current[screen][k] = v.selected
- end
- end
-end
-
--- Revert tag history.
--- @param screen The screen number.
-function tag.history.restore(screen)
- local s = screen or capi.mouse.screen
- local tags = capi.screen[s]:tags()
- for k, t in pairs(tags) do
- t.selected = tag.history.data.past[s][k]
- end
-end
-
---- Return a table with all visible tags
--- @param s Screen number.
--- @return A table with all selected tags.
-function tag.selectedlist(s)
- local screen = s or capi.mouse.screen
- local tags = capi.screen[screen]:tags()
- local vtags = {}
- for i, t in pairs(tags) do
- if t.selected then
- vtags[#vtags + 1] = t
- end
- end
- return vtags
-end
-
---- Return only the first visible tag.
--- @param s Screen number.
-function tag.selected(s)
- return tag.selectedlist(s)[1]
-end
-
---- Set master width factor.
--- @param mwfact Master width factor.
-function tag.setmwfact(mwfact)
- local t = tag.selected()
- if t then
- t.mwfact = mwfact
- end
-end
-
---- Increase master width factor.
--- @param add Value to add to master width factor.
-function tag.incmwfact(add)
- local t = tag.selected()
- if t then
- t.mwfact = t.mwfact + add
- end
-end
-
---- Set the number of master windows.
--- @param nmaster The number of master windows.
-function tag.setnmaster(nmaster)
- local t = tag.selected()
- if t then
- t.nmaster = nmaster
- end
-end
-
---- Increase the number of master windows.
--- @param add Value to add to number of master windows.
-function tag.incnmaster(add)
- local t = tag.selected()
- if t then
- t.nmaster = t.nmaster + add
- end
-end
-
---- Set number of column windows.
--- @param ncol The number of column.
-function tag.setncol(ncol)
- local t = tag.selected()
- if t then
- t.ncol = ncol
- end
-end
-
---- Increase number of column windows.
--- @param add Value to add to number of column windows.
-function tag.incncol(add)
- local t = tag.selected()
- if t then
- t.ncol = t.ncol + add
- end
-end
-
---- View no tag.
--- @param Optional screen number.
-function tag.viewnone(screen)
- local tags = capi.screen[screen or capi.mouse.screen]:tags()
- for i, t in pairs(tags) do
- t.selected = false
- end
-end
-
---- View a tag by its index.
--- @param i The relative index to see.
--- @param screen Optional screen number.
-function tag.viewidx(i, screen)
- local tags = capi.screen[screen or capi.mouse.screen]:tags()
- local sel = tag.selected()
- tag.viewnone()
- for k, t in ipairs(tags) do
- if t == sel then
- tags[cycle(#tags, k + i)].selected = true
- end
- end
-end
-
---- View next tag. This is the same as tag.viewidx(1).
-function tag.viewnext()
- return tag.viewidx(1)
-end
-
---- View previous tag. This is the same a tag.viewidx(-1).
-function tag.viewprev()
- return tag.viewidx(-1)
-end
-
---- View only a tag.
--- @param t The tag object.
-function tag.viewonly(t)
- tag.viewnone(t.screen)
- t.selected = true
-end
-
---- View only a set of tags.
--- @param tags A table with tags to view only.
--- @param screen Optional screen number of the tags.
-function tag.viewmore(tags, screen)
- tag.viewnone(screen)
- for i, t in pairs(tags) do
- t.selected = true
- end
-end
-
---- Move a client to a tag.
--- @param target The tag to move the client to.
--- @param c Optional client to move, otherwise the focused one is used.
-function client.movetotag(target, c)
- local sel = c or capi.client.focus
- if sel then
- -- Check that tag and client screen are identical
- if sel.screen ~= target.screen then return end
- sel:tags({ target })
- end
-end
-
---- Toggle a tag on a client.
--- @param target The tag to toggle.
--- @param c Optional client to toggle, otherwise the focused one is used.
-function client.toggletag(target, c)
- local sel = c or capi.client.focus
- -- Check that tag and client screen are identical
- if sel and sel.screen == target.screen then
- local tags = sel:tags()
- if tags[target] then
- -- If it's the only tag for the window, stop.
- if #tags == 1 then return end
- tags[tags[target]] = nil
- else
- tags[target] = target
- end
- sel:tags(tags)
- end
-end
-
---- Toggle the floating status of a client.
--- @param c Optional client, the focused on if not set.
-function client.togglefloating(c)
- local sel = c or capi.client.focus
- if sel then
- sel.floating = not sel.floating
- end
-end
-
---- Move a client to a screen. Default is next screen, cycling.
--- @param c The client to move.
--- @param s The screen number, default to current + 1.
-function client.movetoscreen(c, s)
- local sel = c or capi.client.focus
- if sel then
- local sc = capi.screen.count()
- if not s then
- s = sel.screen + 1
- end
- if s > sc then s = 1 elseif s < 1 then s = sc end
- sel.screen = s
- capi.mouse.coords(capi.screen[s].coords)
- capi.client.focus = sel
- end
-end
-
---- Get the current layout name.
--- @param screen The screen number.
-function layout.get(screen)
- local t = tag.selected(screen)
- if t then
- return t.layout
- end
-end
-
---- Create a new userhook (for external libs).
--- @param name Hook name.
-function hooks.user.create(name)
- hooks[name] = {}
- hooks[name].callbacks = {}
- hooks[name].register = function (f)
- table.insert(hooks[name].callbacks, f)
- end
- hooks[name].unregister = function (f)
- for k, h in ipairs(hooks[name].callbacks) do
- if h == f then
- table.remove(hooks[name].callbacks, k)
- break
- end
- end
- end
-end
-
---- Call a created userhook (for external libs).
--- @param name Hook name.
-function hooks.user.call(name, ...)
- for name, callback in pairs(hooks[name].callbacks) do
- callback(...)
- end
-end
-
--- Just set an awful mark to a client to move it later.
-local awfulmarked = {}
-hooks.user.create('marked')
-hooks.user.create('unmarked')
-
---- Mark a client, and then call 'marked' hook.
--- @param c The client to mark, the focused one if not specified.
--- @return True if the client has been marked. False if the client was already marked.
-function client.mark (c)
- local cl = c or capi.client.focus
- if cl then
- for k, v in pairs(awfulmarked) do
- if cl == v then
- return false
- end
- end
-
- table.insert(awfulmarked, cl)
-
- -- Call callback
- hooks.user.call('marked', cl)
- return true
- end
-end
-
---- Unmark a client and then call 'unmarked' hook.
--- @param c The client to unmark, or the focused one if not specified.
--- @return True if the client has been unmarked. False if the client was not marked.
-function client.unmark(c)
- local cl = c or capi.client.focus
-
- for k, v in pairs(awfulmarked) do
- if cl == v then
- table.remove(awfulmarked, k)
- hooks.user.call('unmarked', cl)
- return true
- end
- end
-
- return false
-end
-
---- Check if a client is marked.
--- @param c The client to check, or the focused one otherwise.
-function client.ismarked(c)
- local cl = c or capi.client.focus
- if cl then
- for k, v in pairs(awfulmarked) do
- if cl == v then
- return true
- end
- end
- end
- return false
-end
-
---- Toggle a client as marked.
--- @param c The client to toggle mark.
-function client.togglemarked(c)
- local cl = c or capi.client.focus
-
- if not client.mark(c) then
- client.unmark(c)
- end
-end
-
---- Return the marked clients and empty the marked table.
--- @return A table with all marked clients.
-function client.getmarked()
- for k, v in pairs(awfulmarked) do
- hooks.user.call('unmarked', v)
- end
-
- t = awfulmarked
- awfulmarked = {}
- return t
-end
-
---- Change the layout of the current tag.
--- @param layouts A table of layouts.
--- @param i Relative index.
-function layout.inc(layouts, i)
- local t = tag.selected()
- local number_of_layouts = 0
- local rev_layouts = {}
- for i, v in ipairs(layouts) do
- rev_layouts[v] = i
- number_of_layouts = number_of_layouts + 1
- end
- if t then
- local cur_layout = layout.get()
- local new_layout_index = (rev_layouts[cur_layout] + i) % number_of_layouts
- if new_layout_index == 0 then
- new_layout_index = number_of_layouts
- end
- t.layout = layouts[new_layout_index]
- end
-end
-
---- Set the layout of the current tag by name.
--- @param layout Layout name.
-function layout.set(layout)
- local t = tag.selected()
- if t then
- t.layout = layout
- end
-end
-
--- Autodeclare awful.hooks.* functions
--- mapped to awesome hooks.* functions
-for name, hook in pairs(capi.hooks) do
- if name ~= 'timer' then
- hooks[name] = {}
- hooks[name].register = function (f)
- if not hooks[name].callbacks then
- hooks[name].callbacks = {}
- hook(function (...)
- for i, callback in ipairs(hooks[name].callbacks) do
- callback(...)
- end
- end)
- end
-
- table.insert(hooks[name].callbacks, f)
- end
- hooks[name].unregister = function (f)
- if hooks[name].callbacks then
- for k, h in ipairs(hooks[name].callbacks) do
- if h == f then
- table.remove(hooks[name].callbacks, k)
- break
- end
- end
- end
- end
- else
- hooks[name] = {}
- hooks[name].register = function (time, f, runnow)
- if type(time) ~= 'number' or type(f) ~= 'function' or time <= 0 then
- return
- end
- local new_timer
- if hooks[name].timer then
- -- Take the smallest between current and new
- new_timer = math.min(time, hooks[name].timer)
- else
- new_timer = time
- end
- if not hooks[name].callbacks then
- hooks[name].callbacks = {}
- end
- if hooks[name].timer ~= new_timer then
- hooks[name].timer = new_timer
- hook(hooks[name].timer, function (...)
- for i, callback in ipairs(hooks[name].callbacks) do
- callback['counter'] = callback['counter'] + hooks[name].timer
- if callback['counter'] >= callback['timer'] then
- callback['callback'](...)
- callback['counter'] = 0
- end
- end
- end)
- end
-
- if runnow then
- table.insert(hooks[name].callbacks, { callback = f, timer = time, counter = time })
- else
- table.insert(hooks[name].callbacks, { callback = f, timer = time, counter = 0 })
- end
- end
- hooks[name].unregister = function (f)
- if hooks[name].callbacks then
- for k, h in ipairs(hooks[name].callbacks) do
- if h.callback == f then
- table.remove(hooks[name].callbacks, k)
- break
- end
- end
- end
- end
- end
-end
-
---- Spawn a program.
--- @param cmd The command.
--- @param screen The screen where to spawn window.
--- @return The awesome.spawn return value.
-function spawn(cmd, screen)
- if cmd and cmd ~= "" then
- return capi.awesome.spawn(cmd .. "&", screen or capi.mouse.screen)
- end
-end
-
--- Read a program output and returns its output as a string.
--- @param cmd The command to run.
--- @return A string with the program output.
-function pread(cmd)
- if cmd and cmd ~= "" then
- local f = io.popen(cmd, 'r')
- local s = f:read("*all")
- f:close()
- return s
- end
-end
-
---- Eval Lua code.
--- @return The return value of Lua code.
-function eval(s)
- return assert(loadstring("return " .. s))()
-end
-
---- Load history file in history table
--- @param id The prompt history identifier which is the path to the filename
--- @param max Optional parameter, the maximum number of entries in file
-local function prompt_history_check_load(id, max)
- if id and id ~= ""
- and not prompt.history[id] then
- prompt.history[id] = { max = 50, table = {} }
-
- if max then
- prompt.history[id].max = max
- end
-
- local f = io.open(id, "r")
-
- -- Read history file
- if f then
- for line in f:lines() do
- table.insert(prompt.history[id].table, line)
- if #prompt.history[id].table >= prompt.history[id].max then
- break
- end
- end
- end
- end
-end
-
---- Save history table in history file
--- @param id The prompt history identifier
-local function prompt_history_save(id)
- if prompt.history[id] then
- local f = io.open(id, "w")
- if not f then
- local i = 0
- for d in id:gmatch(".-/") do
- i = i + #d
- end
- mkdir(id:sub(1, i - 1))
- f = assert(io.open(id, "w"))
- end
- for i = 1, math.min(#prompt.history[id].table, prompt.history[id].max) do
- f:write(prompt.history[id].table[i] .. "\n")
- end
- f:close()
- end
-end
-
---- Return the number of items in history table regarding the id
--- @param id The prompt history identifier
--- @return the number of items in history table, -1 if history is disabled
-local function prompt_history_items(id)
- if prompt.history[id] then
- return #prompt.history[id].table
- else
- return -1
- end
-end
-
---- Add an entry to the history file
--- @param id The prompt history identifier
--- @param command The command to add
-local function prompt_history_add(id, command)
- if prompt.history[id] then
- if command ~= ""
- and command ~= prompt.history[id].table[#prompt.history[id].table] then
- table.insert(prompt.history[id].table, command)
-
- -- Do not exceed our max_cmd
- if #prompt.history[id].table > prompt.history[id].max then
- table.remove(prompt.history[id].table, 1)
- end
-
- prompt_history_save(id)
- end
- end
-end
-
-
---- Enable programmable bash completion in awful.completion.bash at the price of
--- a slight overhead
--- @param src The bash completion source file, /etc/bash_completion by default.
-function completion.bashcomp_load(src)
- if src then bashcomp_src = src end
- local c = io.popen("/usr/bin/env bash -c 'source " .. bashcomp_src .. "; complete -p'")
- while true do
- local line = c:read("*line")
- if not line then break end
- -- if a bash function is used for completion, register it
- if line:match(".* -F .*") then
- bashcomp_funcs[line:gsub(".* (%S+)$","%1")] = line:gsub(".*-F +(%S+) .*$", "%1")
- end
- end
- c:close()
-end
-
---- Use bash completion system to complete command and filename.
--- @param command The command line.
--- @param cur_pos The cursor position.
--- @param ncomp The element number to complete.
--- @return The new command and the new cursor position.
-function completion.bash(command, cur_pos, ncomp)
- local wstart = 1
- local wend = 1
- local words = {}
- local cword_index = 0
- local cword_start = 0
- local cword_end = 0
- local i = 1
- local comptype = "file"
-
- -- do nothing if we are on a letter, i.e. not at len + 1 or on a space
- if cur_pos ~= #command + 1 and command:sub(cur_pos, cur_pos) ~= " " then
- return command, cur_pos
- elseif #command == 0 then
- return command, cur_pos
- end
-
- while wend <= #command do
- wend = command:find(" ", wstart)
- if not wend then wend = #command + 1 end
- table.insert(words, command:sub(wstart, wend - 1))
- if cur_pos >= wstart and cur_pos <= wend + 1 then
- cword_start = wstart
- cword_end = wend
- cword_index = i
- end
- wstart = wend + 1
- i = i + 1
- end
-
- if cword_index == 1 then
- comptype = "command"
- end
-
- local bash_cmd
- if bashcomp_funcs[words[1]] then
- -- fairly complex command with inline bash script to get the possible completions
- bash_cmd = "/usr/bin/env bash -c 'source " .. bashcomp_src .. "; " ..
- "__print_completions() { for ((i=0;i<${#COMPREPLY[*]};i++)); do echo ${COMPREPLY[i]}; done }; " ..
- "COMP_WORDS=(" .. command .."); COMP_LINE=\"" .. command .. "\"; " ..
- "COMP_COUNT=" .. cur_pos .. "; COMP_CWORD=" .. cword_index-1 .. "; " ..
- bashcomp_funcs[words[1]] .. "; __print_completions | sort -u'"
- else
- bash_cmd = "/usr/bin/env bash -c 'compgen -A " .. comptype .. " " .. words[cword_index] .. "'"
- end
- local c = io.popen(bash_cmd)
- local output = {}
- i = 0
- while true do
- local line = c:read("*line")
- if not line then break end
- table.insert(output, line)
- end
-
- c:close()
-
- -- no completion, return
- if #output == 0 then
- return command, cur_pos
- end
-
- -- cycle
- while ncomp > #output do
- ncomp = ncomp - #output
- end
-
- local str = command:sub(1, cword_start - 1) .. output[ncomp] .. command:sub(cword_end)
- cur_pos = cword_end + #output[ncomp] + 1
-
- return str, cur_pos
-end
-
---- Draw the prompt text with a cursor.
--- @param text The text.
--- @param text_color The text color.
--- @param cursor_color The cursor color.
--- @param cursor_pos The cursor position.
-local function prompt_text_with_cursor(text, text_color, cursor_color, cursor_pos)
- local char
- if not text then text = "" end
- if #text < cursor_pos then
- char = " "
- else
- char = escape(text:sub(cursor_pos, cursor_pos))
- end
- local text_start = escape(text:sub(1, cursor_pos - 1))
- local text_end = escape(text:sub(cursor_pos + 1))
- return text_start .. "" .. char .. "" .. text_end
-end
-
---- Run a prompt in a box.
--- @param args A table with optional arguments: fg_cursor, bg_cursor, prompt.
--- @param textbox The textbox to use for the prompt.
--- @param exe_callback The callback function to call with command as argument when finished.
--- @param completion_callback The callback function to call to get completion.
--- @param history_path Optional parameter: file path where the history should be saved, set nil to disable history
--- @param history_max Optional parameter: set the maximum entries in history file, 50 by default
--- @param done_callback Optional parameter: the callback function to always call without arguments, regardless of whether the prompt was cancelled.
-function prompt.run(args, textbox, exe_callback, completion_callback, history_path, history_max, done_callback)
- if not args then args = {} end
- local command = ""
- local command_before_comp
- local cur_pos_before_comp
- local prettyprompt = args.prompt or ""
- local inv_col = args.fg_cursor or theme.fg_focus or "black"
- local cur_col = args.bg_cursor or theme.bg_focus or "white"
-
- prompt_history_check_load(history_path, history_max)
- local history_index = prompt_history_items(history_path) + 1
- -- The cursor position
- local cur_pos = 1
- -- The completion element to use on completion request.
- local ncomp = 1
- if not textbox or not exe_callback then
- return
- end
- textbox.text = prettyprompt .. prompt_text_with_cursor(text, inv_col, cur_col, cur_pos)
- capi.keygrabber.run(
- function (mod, key)
- -- Get out cases
- if mod.Control then
- if key == "c" or key == "g" then
- textbox.text = ""
- if done_callback then done_callback() end
- return false
- elseif key == "j" or key == "m" then
- textbox.text = ""
- exec_callback(command)
- if done_callback then done_callback() end
- return false
- end
- else
- if key == "Return" then
- textbox.text = ""
- prompt_history_add(history_path, command)
- exe_callback(command)
- if done_callback then done_callback() end
- return false
- elseif key == "Escape" then
- textbox.text = ""
- if done_callback then done_callback() end
- return false
- end
- end
-
- -- Control cases
- if mod.Control then
- if key == "a" then
- cur_pos = 1
- elseif key == "b" then
- if cur_pos > 1 then
- cur_pos = cur_pos - 1
- end
- elseif key == "d" then
- if cur_pos <= #command then
- command = command:sub(1, cur_pos - 1) .. command:sub(cur_pos + 1)
- end
- elseif key == "e" then
- cur_pos = #command + 1
- elseif key == "f" then
- if cur_pos <= #command then
- cur_pos = cur_pos + 1
- end
- elseif key == "h" then
- if cur_pos > 1 then
- command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
- cur_pos = cur_pos - 1
- end
- elseif key == "k" then
- command = command:sub(1, cur_pos - 1)
- elseif key == "u" then
- command = command:sub(cur_pos, #command)
- cur_pos = 1
- elseif key == "w" then
- local wstart = 1
- local wend = 1
- local cword_start = 1
- local cword_end = 1
- while wend < cur_pos do
- wend = command:find(" ", wstart)
- if not wend then wend = #command + 1 end
- if cur_pos >= wstart and cur_pos <= wend + 1 then
- cword_start = wstart
- cword_end = cur_pos - 1
- break
- end
- wstart = wend + 1
- end
- command = command:sub(1, cword_start - 1) .. command:sub(cword_end + 1)
- cur_pos = cword_start
- end
- else
- if completion_callback then
- -- That's tab
- if key:byte() == 9 or key == "ISO_Left_Tab" then
- if key == "ISO_Left_Tab" then
- if ncomp == 1 then return true end
- if ncomp == 2 then
- command = command_before_comp
- textbox.text = prettyprompt .. prompt_text_with_cursor(command_before_comp, inv_col, cur_col, cur_pos)
- return true
- end
-
- ncomp = ncomp - 2
- elseif ncomp == 1 then
- command_before_comp = command
- cur_pos_before_comp = cur_pos
- end
- command, cur_pos = completion_callback(command_before_comp, cur_pos_before_comp, ncomp)
- ncomp = ncomp + 1
- key = ""
- else
- ncomp = 1
- end
- end
-
- -- Typin cases
- if key == "Home" then
- cur_pos = 1
- elseif key == "End" then
- cur_pos = #command + 1
- elseif key == "BackSpace" then
- if cur_pos > 1 then
- command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
- cur_pos = cur_pos - 1
- end
- -- That's DEL
- elseif key:byte() == 127 then
- command = command:sub(1, cur_pos - 1) .. command:sub(cur_pos + 1)
- elseif key == "Left" then
- cur_pos = cur_pos - 1
- elseif key == "Right" then
- cur_pos = cur_pos + 1
- elseif key == "Up" then
- if history_index > 1 then
- history_index = history_index - 1
-
- command = prompt.history[history_path].table[history_index]
- cur_pos = #command + 2
- end
- elseif key == "Down" then
- if history_index < prompt_history_items(history_path) then
- history_index = history_index + 1
-
- command = prompt.history[history_path].table[history_index]
- cur_pos = #command + 2
- elseif history_index == prompt_history_items(history_path) then
- history_index = history_index + 1
-
- command = ""
- cur_pos = 1
- end
- else
- -- len() is UTF-8 aware but #key is not,
- -- so check that we have one UTF-8 char but advance the cursor of # position
- if key:len() == 1 then
- command = command:sub(1, cur_pos - 1) .. key .. command:sub(cur_pos)
- cur_pos = cur_pos + #key
- end
- end
- if cur_pos < 1 then
- cur_pos = 1
- elseif cur_pos > #command + 1 then
- cur_pos = #command + 1
- end
- end
-
- -- Update textbox
- textbox.text = prettyprompt .. prompt_text_with_cursor(command, inv_col, cur_col, cur_pos)
-
- return true
- end)
-end
-
---- Escape a string from XML char.
--- Useful to set raw text in textbox.
--- @param text Text to escape.
--- @return Escape text.
-function escape(text)
- if text then
- text = text:gsub("&", "&")
- text = text:gsub("<", "<")
- text = text:gsub(">", ">")
- text = text:gsub("'", "'")
- text = text:gsub("\"", """)
- end
- return text
-end
-
---- Unescape a string from entities.
--- @param text Text to unescape.
--- @return Unescaped text.
-function unescape(text)
- if text then
- text = text:gsub("&", "&")
- text = text:gsub("<", "<")
- text = text:gsub(">", ">")
- text = text:gsub("'", "'")
- text = text:gsub(""", "\"")
- end
- return text
-end
-
---- Return labels for a taglist widget with all tag from screen.
--- It returns the tag name and set a special
--- foreground and background color for selected tags.
--- @param t The tag.
--- @param args The arguments table.
--- bg_focus The background color for selected tag.
--- fg_focus The foreground color for selected tag.
--- bg_urgent The background color for urgent tags.
--- fg_urgent The foreground color for urgent tags.
--- taglist_squares Optional: set "true" or nil to display the taglist squares.
--- taglist_squares_sel Optional: an user provided image for selected squares.
--- taglist_squares_unsel Optional: an user provided image for unselected squares.
--- @return A string to print.
-function widget.taglist.label.all(t, args)
- if not args then args = {} end
- local fg_focus = args.fg_focus or theme.taglist_fg_focus or theme.fg_focus
- local bg_focus = args.bg_focus or theme.taglist_bg_focus or theme.bg_focus
- local fg_urgent = args.fg_urgent or theme.taglist_fg_urgent or theme.fg_urgent
- local bg_urgent = args.bg_urgent or theme.taglist_bg_urgent or theme.bg_urgent
- local taglist_squares = args.taglist_squares or theme.taglist_squares
- local taglist_squares_sel = args.squares_sel or theme.squares_sel
- local taglist_squares_unsel = args.squares_unsel or theme.squares_unsel
- local text
- local background = ""
- local sel = capi.client.focus
- local bg_color = nil
- local fg_color = nil
- if t.selected then
- bg_color = bg_focus
- fg_color = fg_focus
- end
- if sel and sel:tags()[t] then
- if not taglist_squares or taglist_squares == "true" then
- if taglist_squares_sel then
- background = "resize=\"true\" image=\"" .. taglist_squares_sel .. "\""
- else
- background = "resize=\"true\" image=\"@AWESOME_ICON_PATH@/taglist/squarefw.png\""
- end
- end
- elseif bg_urgent or fg_urgent then
- for k, c in pairs(t:clients()) do
- if not taglist_squares or taglist_squares == "true" then
- if taglist_squares_unsel then
- background = "resize=\"true\" image=\"" .. taglist_squares_unsel .. "\""
- else
- background = "resize=\"true\" image=\"@AWESOME_ICON_PATH@/taglist/squarew.png\""
- end
- end
- if c.urgent then
- bg_color = bg_urgent
- fg_color = fg_urgent
- break
- end
- end
- end
- if bg_color and fg_color then
- text = " "..escape(t.name).." "
- else
- text = " "..escape(t.name).." "
- end
- return text
-end
-
---- Return labels for a taglist widget with all *non empty* tags from screen.
--- It returns the tag name and set a special
--- foreground and background color for selected tags.
--- @param t The tag.
--- @param args The arguments table.
--- bg_focus The background color for selected tag.
--- fg_focus The foreground color for selected tag.
--- bg_urgent The background color for urgent tags.
--- fg_urgent The foreground color for urgent tags.
--- @return A string to print.
-function widget.taglist.label.noempty(t, args)
- if #t:clients() > 0 or t.selected then
- if not args then args = {} end
- local fg_focus = args.fg_focus or theme.taglist_fg_focus or theme.fg_focus
- local bg_focus = args.bg_focus or theme.taglist_bg_focus or theme.bg_focus
- local fg_urgent = args.fg_urgent or theme.taglist_fg_urgent or theme.fg_urgent
- local bg_urgent = args.bg_urgent or theme.taglist_bg_urgent or theme.bg_urgent
- local bg_color = nil
- local fg_color = nil
- local text
-
- if t.selected then
- bg_color = bg_focus
- fg_color = fg_focus
- end
- if bg_urgent and fg_urgent then
- for k, c in pairs(t:clients()) do
- if c.urgent then
- bg_color = bg_urgent
- fg_color = fg_urgent
- break
- end
- end
- end
- if fg_color and bg_color then
- text = " " .. escape(t.name) .. " "
- else
- text = " " .. escape(t.name) .. " "
- end
- return text
- end
-end
-
-local function widget_tasklist_label_common(c, args)
- if not args then args = {} end
- local fg_focus = args.fg_focus or theme.tasklist_fg_focus or theme.fg_focus
- local bg_focus = args.bg_focus or theme.tasklist_bg_focus or theme.bg_focus
- local fg_urgent = args.fg_urgent or theme.tasklist_fg_urgent or theme.fg_urgent
- local bg_urgent = args.bg_urgent or theme.tasklist_bg_urgent or theme.bg_urgent
- local text = ""
- local name
- if c.floating then
- text = ""
- end
- if c.minimized then
- name = escape(c.icon_name) or ""
- else
- name = escape(c.name) or ""
- end
- if capi.client.focus == c then
- if bg_focus and fg_focus then
- text = text .. " "..name.." "
- else
- text = text .. " "..name.." "
- end
- elseif c.urgent and bg_urgent and fg_urgent then
- text = text .. " "..name.." "
- else
- text = text .. " "..name.." "
- end
- return text
-end
-
---- Return labels for a tasklist widget with clients from all tags and screen.
--- It returns the client name and set a special
--- foreground and background color for focused client.
--- It also puts a special icon for floating windows.
--- @param c The client.
--- @param screen The screen we are drawing on.
--- @param args The arguments table.
--- bg_focus The background color for focused client.
--- fg_focus The foreground color for focused client.
--- bg_urgent The background color for urgent clients.
--- fg_urgent The foreground color for urgent clients.
--- @return A string to print.
-function widget.tasklist.label.allscreen(c, screen, args)
- return widget_tasklist_label_common(c, args)
-end
-
---- Return labels for a tasklist widget with clients from all tags.
--- It returns the client name and set a special
--- foreground and background color for focused client.
--- It also puts a special icon for floating windows.
--- @param c The client.
--- @param screen The screen we are drawing on.
--- @param args The arguments table.
--- bg_focus The background color for focused client.
--- fg_focus The foreground color for focused client.
--- bg_urgent The background color for urgent clients.
--- fg_urgent The foreground color for urgent clients.
--- @return A string to print.
-function widget.tasklist.label.alltags(c, screen, args)
- -- Only print client on the same screen as this widget
- if c.screen ~= screen then return end
- return widget_tasklist_label_common(c, args)
-end
-
---- Return labels for a tasklist widget with clients from currently selected tags.
--- It returns the client name and set a special
--- foreground and background color for focused client.
--- It also puts a special icon for floating windows.
--- @param c The client.
--- @param screen The screen we are drawing on.
--- @param args The arguments table.
--- bg_focus The background color for focused client.
--- fg_focus The foreground color for focused client.
--- bg_urgent The background color for urgent clients.
--- fg_urgent The foreground color for urgent clients.
--- @return A string to print.
-function widget.tasklist.label.currenttags(c, screen, args)
- -- Only print client on the same screen as this widget
- if c.screen ~= screen then return end
- for k, t in ipairs(capi.screen[screen]:tags()) do
- if t.selected and c:tags()[t] then
- return widget_tasklist_label_common(c, args)
- end
- end
-end
-
---- Create a standard titlebar.
--- @param c The client.
--- @param args Arguments.
--- fg: the foreground color.
--- bg: the background color.
--- fg_focus: the foreground color for focused window.
--- fg_focus: the background color for focused window.
--- width: the titlebar width
-function titlebar.add(c, args)
- if not c or c.type ~= "normal" then return end
- if not args then args = {} end
- -- Store colors
- titlebar.data[c] = {}
- titlebar.data[c].fg = args.fg or theme.titlebar_fg_normal or theme.fg_normal
- titlebar.data[c].bg = args.bg or theme.titlebar_bg_normal or theme.bg_normal
- titlebar.data[c].fg_focus = args.fg_focus or theme.titlebar_fg_focus or theme.fg_focus
- titlebar.data[c].bg_focus = args.bg_focus or theme.titlebar_bg_focus or theme.bg_focus
- titlebar.data[c].width = args.width
-
- -- Built args
- local targs = {}
- if args.fg then targs.fg = args.fg end
- if args.bg then targs.bg = args.bg end
- local tb = capi.wibox(targs)
-
- local title = capi.widget({ type = "textbox", name = "title", align = "flex" })
- title.text = " " .. escape(c.name) .. " "
- local bts =
- {
- capi.button({ }, 1, function (t) t.client:mouse_move() end),
- capi.button({ args.modkey }, 3, function (t) t.client:mouse_resize() end)
- }
- title:buttons(bts)
-
- local appicon = capi.widget({ type = "imagebox", name = "appicon", align = "left" })
- appicon.image = c.icon
-
- if theme.titlebar_close_button == "true" then
- local closef = widget.button({ name = "closef", align = "right",
- image = theme.titlebar_close_button_focus
- or theme.titlebar_close_button_img_focus
- or "@AWESOME_ICON_PATH@/titlebar/closer.png" })
- local close = widget.button({ name = "close", align = "right",
- image = theme.titlebar_close_button_normal
- or theme.titlebar_close_button_img_normal
- or "@AWESOME_ICON_PATH@/titlebar/close.png" })
-
- -- Bind kill button
- local b = capi.button({ }, 1, nil, function (t) t.client:kill() end)
- local bts = closef:buttons()
- bts[#bts + 1] = b
- closef:buttons(bts)
-
- bts = close:buttons()
- bts[#bts + 1] = b
- close:buttons(bts)
-
- tb:widgets({
- appicon,
- title,
- closef, close
- })
- else
- tb:widgets({
- appicon,
- title
- })
- end
-
- c.titlebar = tb
-
- titlebar.update(c)
- titlebar.update(c, "geometry")
-end
-
---- Update a titlebar. This should be called in some hooks.
--- @param c The client to update.
--- @param prop The property name which has changed.
-function titlebar.update(c, prop)
- if c.titlebar and titlebar.data[c] then
- local widgets = c.titlebar:widgets()
- local title, close, closef
- for k, v in ipairs(widgets) do
- if v.name == "title" then title = v
- elseif v.name == "close" then close = v
- elseif v.name == "closef" then closef = v
- elseif v.name == "appicon" then appicon = v end
- if title and close and closef and appicon then break end
- end
- if prop == "name" then
- if title then
- title.text = " " .. escape(c.name) .. " "
- end
- elseif prop == "icon" then
- if appicon then
- appicon.image = c.icon
- end
- elseif prop == "geometry" then
- if titlebar.data[c].width then
- if c.titlebar.position == "top"
- or c.titlebar.position == "bottom" then
- local w = math.min(titlebar.data[c].width, c:coords().width + 2 * c.border_width)
- c.titlebar:geometry({ width = w })
- else
- local w = math.min(titlebar.data[c].width, c:coords().height + 2 * c.border_width)
- c.titlebar:geometry({ height = w })
- end
- end
- end
- if capi.client.focus == c then
- c.titlebar.fg = titlebar.data[c].fg_focus
- c.titlebar.bg = titlebar.data[c].bg_focus
- if closef then closef.visible = true end
- if close then close.visible = false end
- else
- c.titlebar.fg = titlebar.data[c].fg
- c.titlebar.bg = titlebar.data[c].bg
- if closef then closef.visible = false end
- if close then close.visible = true end
- end
- end
-end
-
---- Remove a titlebar from a client.
--- @param c The client.
-function titlebar.remove(c)
- c.titlebar = nil
- titlebar.data[c] = nil
-end
-
---- Set the beautiful theme if any.
--- @param The beautiful theme.
-function beautiful.register(btheme)
- if btheme then
- theme = btheme
- else
- theme = {}
- end
-end
-
---- Create a button widget. When clicked, the image is deplaced to make it like
--- a real button.
--- @param args Standard widget table arguments, plus image for the image path.
--- @return A textbox widget configured as a button.
-function widget.button(args)
- if not args then return end
- args.type = "textbox"
- local w = capi.widget(args)
- local img_release = ""
- local img_press = ""
- w.text = img_release
- w:buttons({ capi.button({}, 1, function () w.text = img_press end, function () w.text = img_release end) })
- function w.mouse_leave(s) w.text = img_release end
- function w.mouse_enter(s) if capi.mouse.coords().buttons[1] then w.text = img_press end end
- return w
-end
-
---- Create a button widget which will launch a command.
--- @param args Standard widget table arguments, plus image for the image path
--- and command for the command to run on click.
--- @return A launcher widget.
-function widget.launcher(args)
- if not args.command then return end
- local w = widget.button(args)
- local b = w:buttons()
- b[#b + 1] = capi.button({}, 1, nil, function () spawn(args.command) end)
- w:buttons(b)
- return w
-end
-
---- Check if an area intersect another area.
--- @param a The area.
--- @param b The other area.
--- @return True if they intersect, false otherwise.
-local function area_intersect_area(a, b)
- return (b.x < a.x + a.width
- and b.x + b.width > a.x
- and b.y < a.y + a.height
- and b.y + b.height > a.y)
-end
-
---- Get the intersect area between a and b.
--- @param a The area.
--- @param b The other area.
--- @return The intersect area.
-local function area_intersect_area_get(a, b)
- local g = {}
- g.x = math.max(a.x, b.x)
- g.y = math.max(a.y, b.y)
- g.width = math.min(a.x + a.width, b.x + b.width) - g.x
- g.height = math.min(a.y + a.height, b.y + b.height) - g.y
- return g
-end
-
---- Remove an area from a list, splitting the space between several area that
--- can overlap.
--- @param areas Table of areas.
--- @param elem Area to remove.
--- @return The new area list.
-local function area_remove(areas, elem)
- local newareas = areas
- for i, r in ipairs(areas) do
- -- Check if the 'elem' intersect
- if area_intersect_area(r, elem) then
- -- It does? remove it
- table.remove(areas, i)
- local inter = area_intersect_area_get(r, elem)
-
- if inter.x > r.x then
- table.insert(newareas, {
- x = r.x,
- y = r.y,
- width = inter.x - r.x,
- height = r.height
- })
- end
-
- if inter.y > r.y then
- table.insert(newareas, {
- x = r.x,
- y = r.y,
- width = r.width,
- height = inter.y - r.y
- })
- end
-
- if inter.x + inter.width < r.x + r.width then
- table.insert(newareas, {
- x = inter.x + inter.width,
- y = r.y,
- width = (r.x + r.width) - (inter.x + inter.width),
- height = r.height
- })
- end
-
- if inter.y + inter.height < r.y + r.height then
- table.insert(newareas, {
- x = r.x,
- y = inter.y + inter.height,
- width = r.width,
- height = (r.y + r.height) - (inter.y + inter.height)
- })
- end
- end
- end
-
- return newareas
-end
-
---- Place the client without it being outside the screen.
--- @param c The client.
-function placement.no_offscreen(c)
- local geometry = c:fullcoords()
- local screen_geometry = capi.screen[c.screen].workarea
-
- if geometry.x + geometry.width > screen_geometry.x + screen_geometry.width then
- geometry.x = screen_geometry.x + screen_geometry.width - geometry.width
- elseif geometry.x < screen_geometry.x then
- geometry.x = screen_geometry.x
- end
-
- if geometry.y + geometry.height > screen_geometry.y + screen_geometry.height then
- geometry.y = screen_geometry.y + screen_geometry.height - geometry.height
- elseif geometry.y < screen_geometry.y then
- geometry.y = screen_geometry.y
- end
-
- c:fullcoords(geometry)
-end
-
---- Place the client where there's place available with minimum overlap.
--- @param c The client.
-function placement.no_overlap(c)
- local cls = client.visible(c.screen)
- local layout = layout.get()
- local areas = { capi.screen[c.screen].workarea }
- local coords = c:coords()
- local fullcoords = c:fullcoords()
- for i, cl in pairs(cls) do
- if cl ~= c and (cl.floating or layout == "floating") then
- areas = area_remove(areas, cl:fullcoords())
- end
- end
-
- -- Look for available space
- local found = false
- local new = { x = coords.x, y = coords.y, width = 0, height = 0 }
- for i, r in ipairs(areas) do
- if r.width >= fullcoords.width
- and r.height >= fullcoords.height
- and r.width * r.height > new.width * new.height then
- found = true
- new = r
- end
- end
-
- -- We did not foudn an area with enough space for our size:
- -- just take the biggest available one and go in
- if not found then
- for i, r in ipairs(areas) do
- if r.width * r.height > new.width * new.height then
- new = r
- end
- end
- end
-
- -- Restore height and width
- new.width = coords.width
- new.height = coords.height
-
- c:coords(new)
-end
-
---- Place the client under the mouse.
--- @param c The client.
-function placement.under_mouse(c)
- local c_coords = c:coords()
- local m_coords = capi.mouse.coords()
- c:coords({ x = m_coords.x - c_coords.width / 2,
- y = m_coords.y - c_coords.height / 2 })
-end
-
--- Register standards hooks
-hooks.arrange.register(tag.history.update)
-
-hooks.focus.register(client.focus.history.add)
-hooks.unmanage.register(client.focus.history.delete)
-hooks.unmanage.register(client_maximize_clean)
-
-hooks.focus.register(titlebar.update)
-hooks.unfocus.register(titlebar.update)
-hooks.property.register(titlebar.update)
-hooks.unmanage.register(titlebar.remove)
-
-hooks.property.register(client.urgent.stack.add)
-hooks.focus.register(client.urgent.stack.delete)
-hooks.unmanage.register(client.urgent.stack.delete)
-
--- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
diff --git a/lib/awful/beautiful.lua.in b/lib/awful/beautiful.lua.in
new file mode 100644
index 000000000..dcf597d03
--- /dev/null
+++ b/lib/awful/beautiful.lua.in
@@ -0,0 +1,31 @@
+---------------------------------------------------------------------------
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @release @AWESOME_VERSION@
+---------------------------------------------------------------------------
+
+local package = package
+
+--- Beautiful module for awful
+module("awful.beautiful")
+
+-- Exported variable handling theme
+theme = {}
+
+--- Set the beautiful theme if any.
+-- @param The beautiful theme.
+function register(btheme)
+ if btheme then
+ theme = btheme
+ else
+ theme = {}
+ end
+end
+
+--- Get the current registerde them.
+-- @return The current theme table.
+function get()
+ return package.loaded.awful.beautiful.theme
+end
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
diff --git a/lib/awful/client.lua.in b/lib/awful/client.lua.in
new file mode 100644
index 000000000..a3b0d981e
--- /dev/null
+++ b/lib/awful/client.lua.in
@@ -0,0 +1,529 @@
+---------------------------------------------------------------------------
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @release @AWESOME_VERSION@
+---------------------------------------------------------------------------
+
+-- Grab environment we need
+local hooks = require("awful.hooks")
+local util = require("awful.util")
+local pairs = pairs
+local ipairs = ipairs
+local table = table
+local math = math
+local otable = otable
+local capi =
+{
+ client = client,
+ mouse = mouse,
+ screen = screen,
+}
+
+--- Client module for awful
+module("awful.client")
+
+-- Local variable handling theme
+local theme = {}
+
+-- mapping of command/completion function
+local bashcomp_funcs = {}
+local bashcomp_src = "/etc/bash_completion"
+
+-- Private data
+local data = {}
+data.maximize = otable()
+data.focus = {}
+data.urgent = {}
+data.marked = {}
+
+-- Urgent functions
+urgent = {}
+focus = {}
+focus.history = {}
+
+-- User hooks
+hooks.user.create('marked')
+hooks.user.create('unmarked')
+
+--- Get the first client that got the urgent hint.
+-- @return The first urgent client.
+function urgent.get()
+ if #data.urgent > 0 then
+ return #data.urgent[1]
+ else
+ -- fallback behaviour: iterate through clients and get the first urgent
+ local clients = capi.client.get()
+ for k, cl in pairs(clients) do
+ if cl.urgent then
+ return cl
+ end
+ end
+ end
+end
+
+--- Jump to the client that received the urgent hint first.
+function urgent.jumpto()
+ local c = urgent.get()
+ if c then
+ local s = capi.client.focus and capi.client.focus.screen or capi.mouse.screen
+ -- focus the screen
+ if s ~= c.screen then
+ capi.mouse.screen = c.screen
+ end
+ -- focus the tag
+ tag.viewonly(c:tags()[1])
+ -- focus the client
+ capi.client.focus = c
+ c:raise()
+ end
+end
+
+--- Adds client to urgent stack.
+-- @param c The client object.
+-- @param prop The property which is updated.
+function urgent.add(c, prop)
+ if prop == "urgent" and c.urgent then
+ table.insert(data.urgent, c)
+ end
+end
+
+--- Remove client from urgent stack.
+-- @param c The client object.
+function urgent.delete(c)
+ for k, cl in ipairs(data.urgent) do
+ if c == cl then
+ table.remove(data.urgent, k)
+ break
+ end
+ end
+end
+
+--- Remove a client from the focus history
+-- @param c The client that must be removed.
+function focus.history.delete(c)
+ for k, v in ipairs(data.focus) do
+ if v == c then
+ table.remove(data.focus, k)
+ break
+ end
+ end
+end
+
+--- Filter out window that we do not want handled by focus.
+-- This usually means that desktop, dock and splash windows are
+-- not registered and cannot get focus.
+-- @param c A client.
+-- @return The same client if it's ok, nil otherwise.
+function focus.filter(c)
+ if c.type == "desktop"
+ or c.type == "dock"
+ or c.type == "splash" then
+ return nil
+ end
+ return c
+end
+
+--- Update client focus history.
+-- @param c The client that has been focused.
+function focus.history.add(c)
+ if focus.filter(c) then
+ -- Remove the client if its in stack
+ focus.history.delete(c)
+ -- Record the client has latest focused
+ table.insert(data.focus, 1, c)
+ end
+end
+
+--- Get the latest focused client for a screen in history.
+-- @param screen The screen number to look for.
+-- @param idx The index: 0 will return first candidate,
+-- 1 will return second, etc.
+-- @return A client.
+function focus.history.get(screen, idx)
+ -- When this counter is equal to idx, we return the client
+ local counter = 0
+ local vc = visible(screen)
+ for k, c in ipairs(data.focus) do
+ if c.screen == screen then
+ for j, vcc in ipairs(vc) do
+ if vcc == c then
+ if counter == idx then
+ return c
+ end
+ -- We found one, increment the counter only.
+ counter = counter + 1
+ break
+ end
+ end
+ end
+ end
+ -- Argh nobody found in history, give the first one visible if there is one
+ if counter == 0 then
+ return vc[1]
+ end
+end
+
+--- Focus the previous client in history.
+function focus.history.previous()
+ local sel = capi.client.focus
+ local s
+ if sel then
+ s = sel.screen
+ else
+ s = capi.mouse.screen
+ end
+ local c = focus.history.get(s, 1)
+ if c then capi.client.focus = c end
+end
+
+--- Get visible clients from a screen.
+-- @param The screen number, or nil for all screen.
+-- @return A table with all visible clients.
+function visible(screen)
+ local cls = capi.client.get(screen)
+ local vcls = {}
+ for k, c in pairs(cls) do
+ if c:isvisible() then
+ table.insert(vcls, c)
+ end
+ end
+ return vcls
+end
+
+--- Get a client by its relative index to the focused window.
+-- @usage Set i to 1 to get next, -1 to get previous.
+-- @param i The index.
+-- @param c Optional client.
+-- @return A client, or nil if no client is available.
+function next(i, c)
+ -- Get currently focused client
+ local sel = c or capi.client.focus
+ if sel then
+ -- Get all visible clients
+ local cls = visible(sel.screen)
+ -- Remove all no-normal clients
+ for idx, c in ipairs(cls) do
+ if not focus.filter(c) then
+ table.remove(cls, idx)
+ end
+ end
+ -- Loop upon each client
+ for idx, c in ipairs(cls) do
+ if c == sel then
+ -- Cycle
+ return cls[util.cycle(#cls, idx + i)]
+ end
+ end
+ end
+end
+
+--- Return true whether client B is in the right direction
+-- compared to client A.
+-- @param dir The direction.
+-- @param cA The first client.
+-- @param cB The second client.
+-- @return True if B is in the direction of A.
+local function is_in_direction(dir, cA, cB)
+ if dir == "up" then
+ return cA['y'] > cB['y']
+ elseif dir == "down" then
+ return cA['y'] < cB['y']
+ elseif dir == "left" then
+ return cA['x'] > cB['x']
+ elseif dir == "right" then
+ return cA['x'] < cB['x']
+ end
+ return false
+end
+
+--- Calculate distance between two points.
+-- i.e: if we want to move to the right, we will take the right border
+-- of the currently focused client and the left side of the checked client.
+-- This avoid the focus of an upper client when you move to the right in a
+-- tilebottom layout with nmaster=2 and 5 clients open, for instance.
+-- @param dir The direction.
+-- @param cA The first client.
+-- @param cB The second client.
+-- @return The distance between the clients.
+local function calculate_distance(dir, cA, cB)
+ local xA = cA['x']
+ local xB = cB['x']
+ local yA = cA['y']
+ local yB = cB['y']
+
+ if dir == "up" then
+ yB = yB + cB['height']
+ elseif dir == "down" then
+ yA = yA + cA['height']
+ elseif dir == "left" then
+ xB = xB + cB['width']
+ elseif dir == "right" then
+ xA = xA + cA['width']
+ end
+
+ return math.sqrt(math.pow(xB - xA, 2) + math.pow(yB - yA, 2))
+end
+
+--- Focus a client by the given direction.
+-- @param dir The direction, can be either "up", "down", "left" or "right".
+-- @param c Optional client.
+function focus.bydirection(dir, c)
+ local sel = c or capi.client.focus
+ if sel then
+ local coords = sel:coords()
+ local dist, dist_min
+ local target = nil
+ local cls = visible(sel.screen)
+
+ -- We check each client.
+ for i, c in ipairs(cls) do
+ -- Check coords to see if client is located in the right direction.
+ if is_in_direction(dir, coords, c:coords()) then
+
+ -- Calculate distance between focused client and checked client.
+ dist = calculate_distance(dir, coords, c:coords())
+
+ -- If distance is shorter then keep the client.
+ if not target or dist < dist_min then
+ target = c
+ dist_min = dist
+ end
+ end
+ end
+
+ -- If we found a client to focus, then do it.
+ if target then
+ capi.client.focus = target
+ end
+ end
+end
+
+function focusbyidx(i, c)
+ util.deprecate()
+ return focus.byidx(i, c)
+end
+
+--- Focus a client by its relative index.
+-- @param i The index.
+-- @param c Optional client.
+function focus.byidx(i, c)
+ local target = next(i, c)
+ if target then
+ capi.client.focus = target
+ end
+end
+
+--- Swap a client by its relative index.
+-- @param i The index.
+-- @param c Optional client, otherwise focused one is used.
+function swap(i, c)
+ local sel = c or capi.client.focus
+ local target = next(i, sel)
+ if target then
+ target:swap(sel)
+ end
+end
+
+--- Get the master window.
+-- @param screen Optional screen number, otherwise screen mouse is used.
+-- @return The master window.
+function master(screen)
+ local s = screen or capi.mouse.screen
+ return visible(s)[1]
+end
+
+-- Set the client as slave: put it at the end of other windows.
+-- @param c The window to set as slave.
+-- @return
+function setslave(c)
+ local cls = visible(c.screen)
+ for k, v in pairs(cls) do
+ c:swap(v)
+ end
+end
+
+--- Move/resize a client relative to current coordinates.
+-- @param x The relative x coordinate.
+-- @param y The relative y coordinate.
+-- @param w The relative width.
+-- @param h The relative height.
+-- @param c The optional client, otherwise focused one is used.
+function moveresize(x, y, w, h, c)
+ local sel = c or capi.client.focus
+ local coords = sel:coords()
+ coords['x'] = coords['x'] + x
+ coords['y'] = coords['y'] + y
+ coords['width'] = coords['width'] + w
+ coords['height'] = coords['height'] + h
+ sel:coords(coords)
+end
+
+--- Maximize a client to use the full workarea.
+-- @param c A client, or the focused one if nil.
+function maximize(c)
+ local sel = c or capi.client.focus
+ if sel then
+ local ws = capi.screen[sel.screen].workarea
+ ws.width = ws.width - 2 * sel.border_width
+ ws.height = ws.height - 2 * sel.border_width
+ if sel.floating and client.data.maximize[sel] then
+ sel.floating = client.data.maximize[sel].floating
+ if sel.floating then
+ sel:coords(client.data.maximize[sel].coords)
+ end
+ data.maximize[sel] = nil
+ else
+ data.maximize[sel] = { coords = sel:coords(), floating = sel.floating }
+ sel.floating = true
+ sel:coords(ws)
+ end
+ end
+end
+
+
+--- Erase eventual client data in maximize.
+-- @param c The client.
+local function maximize_clean(c)
+ data.maximize[c] = nil
+end
+
+--- Move a client to a tag.
+-- @param target The tag to move the client to.
+-- @param c Optional client to move, otherwise the focused one is used.
+function movetotag(target, c)
+ local sel = c or capi.client.focus
+ if sel then
+ -- Check that tag and client screen are identical
+ if sel.screen ~= target.screen then return end
+ sel:tags({ target })
+ end
+end
+
+--- Toggle a tag on a client.
+-- @param target The tag to toggle.
+-- @param c Optional client to toggle, otherwise the focused one is used.
+function toggletag(target, c)
+ local sel = c or capi.client.focus
+ -- Check that tag and client screen are identical
+ if sel and sel.screen == target.screen then
+ local tags = sel:tags()
+ if tags[target] then
+ -- If it's the only tag for the window, stop.
+ if #tags == 1 then return end
+ tags[tags[target]] = nil
+ else
+ tags[target] = target
+ end
+ sel:tags(tags)
+ end
+end
+
+--- Toggle the floating status of a client.
+-- @param c Optional client, the focused on if not set.
+function togglefloating(c)
+ local sel = c or capi.client.focus
+ if sel then
+ sel.floating = not sel.floating
+ end
+end
+
+--- Move a client to a screen. Default is next screen, cycling.
+-- @param c The client to move.
+-- @param s The screen number, default to current + 1.
+function movetoscreen(c, s)
+ local sel = c or capi.client.focus
+ if sel then
+ local sc = capi.screen.count()
+ if not s then
+ s = sel.screen + 1
+ end
+ if s > sc then s = 1 elseif s < 1 then s = sc end
+ sel.screen = s
+ capi.mouse.coords(capi.screen[s].coords)
+ capi.client.focus = sel
+ end
+end
+
+--- Mark a client, and then call 'marked' hook.
+-- @param c The client to mark, the focused one if not specified.
+-- @return True if the client has been marked. False if the client was already marked.
+function mark(c)
+ local cl = c or capi.client.focus
+ if cl then
+ for k, v in pairs(data.marked) do
+ if cl == v then
+ return false
+ end
+ end
+
+ table.insert(data.marked, cl)
+
+ -- Call callback
+ hooks.user.call('marked', cl)
+ return true
+ end
+end
+
+--- Unmark a client and then call 'unmarked' hook.
+-- @param c The client to unmark, or the focused one if not specified.
+-- @return True if the client has been unmarked. False if the client was not marked.
+function unmark(c)
+ local cl = c or capi.client.focus
+
+ for k, v in pairs(data.marked) do
+ if cl == v then
+ table.remove(data.marked, k)
+ hooks.user.call('unmarked', cl)
+ return true
+ end
+ end
+
+ return false
+end
+
+--- Check if a client is marked.
+-- @param c The client to check, or the focused one otherwise.
+function ismarked(c)
+ local cl = c or capi.client.focus
+ if cl then
+ for k, v in pairs(data.marked) do
+ if cl == v then
+ return true
+ end
+ end
+ end
+ return false
+end
+
+--- Toggle a client as marked.
+-- @param c The client to toggle mark.
+function togglemarked(c)
+ local cl = c or capi.client.focus
+
+ if not client.mark(c) then
+ client.unmark(c)
+ end
+end
+
+--- Return the marked clients and empty the marked table.
+-- @return A table with all marked clients.
+function getmarked()
+ for k, v in pairs(data.marked) do
+ hooks.user.call('unmarked', v)
+ end
+
+ t = data.marked
+ data.marked = {}
+ return t
+end
+
+-- Register standards hooks
+hooks.focus.register(focus.history.add)
+hooks.unmanage.register(focus.history.delete)
+hooks.unmanage.register(maximize_clean)
+
+hooks.property.register(urgent.add)
+hooks.focus.register(urgent.delete)
+hooks.unmanage.register(urgent.delete)
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
diff --git a/lib/awful/completion.lua.in b/lib/awful/completion.lua.in
new file mode 100644
index 000000000..2d167594d
--- /dev/null
+++ b/lib/awful/completion.lua.in
@@ -0,0 +1,112 @@
+---------------------------------------------------------------------------
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @release @AWESOME_VERSION@
+---------------------------------------------------------------------------
+
+-- Grab environment we need
+local io = io
+local table = table
+
+--- Completion module for awful
+module("awful.completion")
+
+-- mapping of command/completion function
+local bashcomp_funcs = {}
+local bashcomp_src = "/etc/bash_completion"
+
+--- Enable programmable bash completion in awful.completion.bash at the price of
+-- a slight overhead
+-- @param src The bash completion source file, /etc/bash_completion by default.
+function bashcomp_load(src)
+ if src then bashcomp_src = src end
+ local c = io.popen("/usr/bin/env bash -c 'source " .. bashcomp_src .. "; complete -p'")
+ while true do
+ local line = c:read("*line")
+ if not line then break end
+ -- if a bash function is used for completion, register it
+ if line:match(".* -F .*") then
+ bashcomp_funcs[line:gsub(".* (%S+)$","%1")] = line:gsub(".*-F +(%S+) .*$", "%1")
+ end
+ end
+ c:close()
+end
+
+--- Use bash completion system to complete command and filename.
+-- @param command The command line.
+-- @param cur_pos The cursor position.
+-- @param ncomp The element number to complete.
+-- @return The new command and the new cursor position.
+function bash(command, cur_pos, ncomp)
+ local wstart = 1
+ local wend = 1
+ local words = {}
+ local cword_index = 0
+ local cword_start = 0
+ local cword_end = 0
+ local i = 1
+ local comptype = "file"
+
+ -- do nothing if we are on a letter, i.e. not at len + 1 or on a space
+ if cur_pos ~= #command + 1 and command:sub(cur_pos, cur_pos) ~= " " then
+ return command, cur_pos
+ elseif #command == 0 then
+ return command, cur_pos
+ end
+
+ while wend <= #command do
+ wend = command:find(" ", wstart)
+ if not wend then wend = #command + 1 end
+ table.insert(words, command:sub(wstart, wend - 1))
+ if cur_pos >= wstart and cur_pos <= wend + 1 then
+ cword_start = wstart
+ cword_end = wend
+ cword_index = i
+ end
+ wstart = wend + 1
+ i = i + 1
+ end
+
+ if cword_index == 1 then
+ comptype = "command"
+ end
+
+ local bash_cmd
+ if bashcomp_funcs[words[1]] then
+ -- fairly complex command with inline bash script to get the possible completions
+ bash_cmd = "/usr/bin/env bash -c 'source " .. bashcomp_src .. "; " ..
+ "__print_completions() { for ((i=0;i<${#COMPREPLY[*]};i++)); do echo ${COMPREPLY[i]}; done }; " ..
+ "COMP_WORDS=(" .. command .."); COMP_LINE=\"" .. command .. "\"; " ..
+ "COMP_COUNT=" .. cur_pos .. "; COMP_CWORD=" .. cword_index-1 .. "; " ..
+ bashcomp_funcs[words[1]] .. "; __print_completions | sort -u'"
+ else
+ bash_cmd = "/usr/bin/env bash -c 'compgen -A " .. comptype .. " " .. words[cword_index] .. "'"
+ end
+ local c = io.popen(bash_cmd)
+ local output = {}
+ i = 0
+ while true do
+ local line = c:read("*line")
+ if not line then break end
+ table.insert(output, line)
+ end
+
+ c:close()
+
+ -- no completion, return
+ if #output == 0 then
+ return command, cur_pos
+ end
+
+ -- cycle
+ while ncomp > #output do
+ ncomp = ncomp - #output
+ end
+
+ local str = command:sub(1, cword_start - 1) .. output[ncomp] .. command:sub(cword_end)
+ cur_pos = cword_end + #output[ncomp] + 1
+
+ return str, cur_pos
+end
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
diff --git a/lib/awful/hooks.lua.in b/lib/awful/hooks.lua.in
new file mode 100644
index 000000000..794f3c05a
--- /dev/null
+++ b/lib/awful/hooks.lua.in
@@ -0,0 +1,125 @@
+---------------------------------------------------------------------------
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @release @AWESOME_VERSION@
+---------------------------------------------------------------------------
+
+-- Grab environment we need
+local pairs = pairs
+local table = table
+local ipairs = ipairs
+local type = type
+local math = math
+local capi =
+{
+ hooks = hooks
+}
+
+--- Hooks module for awful
+module("awful.hooks")
+
+-- User hook functions
+user = {}
+
+--- Create a new userhook (for external libs).
+-- @param name Hook name.
+function user.create(name)
+ _M[name] = {}
+ _M[name].callbacks = {}
+ _M[name].register = function (f)
+ table.insert(_M[name].callbacks, f)
+ end
+ _M[name].unregister = function (f)
+ for k, h in ipairs(_M[name].callbacks) do
+ if h == f then
+ table.remove(_M[name].callbacks, k)
+ break
+ end
+ end
+ end
+end
+
+--- Call a created userhook (for external libs).
+-- @param name Hook name.
+function user.call(name, ...)
+ for name, callback in pairs(_M[name].callbacks) do
+ callback(...)
+ end
+end
+
+-- Autodeclare awful.hooks.* functions
+-- mapped to awesome hooks.* functions
+for name, hook in pairs(capi.hooks) do
+ if name ~= 'timer' then
+ _M[name] = {}
+ _M[name].register = function (f)
+ if not _M[name].callbacks then
+ _M[name].callbacks = {}
+ hook(function (...)
+ for i, callback in ipairs(_M[name].callbacks) do
+ callback(...)
+ end
+ end)
+ end
+
+ table.insert(_M[name].callbacks, f)
+ end
+ _M[name].unregister = function (f)
+ if _M[name].callbacks then
+ for k, h in ipairs(_M[name].callbacks) do
+ if h == f then
+ table.remove(_M[name].callbacks, k)
+ break
+ end
+ end
+ end
+ end
+ else
+ _M[name] = {}
+ _M[name].register = function (time, f, runnow)
+ if type(time) ~= 'number' or type(f) ~= 'function' or time <= 0 then
+ return
+ end
+ local new_timer
+ if _M[name].timer then
+ -- Take the smallest between current and new
+ new_timer = math.min(time, _M[name].timer)
+ else
+ new_timer = time
+ end
+ if not _M[name].callbacks then
+ _M[name].callbacks = {}
+ end
+ if _M[name].timer ~= new_timer then
+ _M[name].timer = new_timer
+ hook(_M[name].timer, function (...)
+ for i, callback in ipairs(_M[name].callbacks) do
+ callback['counter'] = callback['counter'] + _M[name].timer
+ if callback['counter'] >= callback['timer'] then
+ callback['callback'](...)
+ callback['counter'] = 0
+ end
+ end
+ end)
+ end
+
+ if runnow then
+ table.insert(_M[name].callbacks, { callback = f, timer = time, counter = time })
+ else
+ table.insert(_M[name].callbacks, { callback = f, timer = time, counter = 0 })
+ end
+ end
+ _M[name].unregister = function (f)
+ if _M[name].callbacks then
+ for k, h in ipairs(_M[name].callbacks) do
+ if h.callback == f then
+ table.remove(_M[name].callbacks, k)
+ break
+ end
+ end
+ end
+ end
+ end
+end
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
diff --git a/lib/awful/init.lua.in b/lib/awful/init.lua.in
new file mode 100644
index 000000000..2c7981472
--- /dev/null
+++ b/lib/awful/init.lua.in
@@ -0,0 +1,23 @@
+---------------------------------------------------------------------------
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @release @AWESOME_VERSION@
+---------------------------------------------------------------------------
+
+local beautiful = require("awful.beautiful")
+local client = require("awful.client")
+local completion = require("awful.completion")
+local hooks = require("awful.hooks")
+local layout = require("awful.layout")
+local placement = require("awful.placement")
+local prompt = require("awful.prompt")
+local screen = require("awful.screen")
+local tag = require("awful.tag")
+local titlebar = require("awful.titlebar")
+local util = require("awful.util")
+local widget = require("awful.widget")
+
+--- awful: AWesome Functions very UsefuL
+module("awful")
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
diff --git a/lib/awful/layout.lua.in b/lib/awful/layout.lua.in
new file mode 100644
index 000000000..a1f23bfa9
--- /dev/null
+++ b/lib/awful/layout.lua.in
@@ -0,0 +1,53 @@
+---------------------------------------------------------------------------
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @release @AWESOME_VERSION@
+---------------------------------------------------------------------------
+
+-- Grab environment we need
+local ipairs = ipairs
+local tag = require("awful.tag")
+
+--- Layout module for awful
+module("awful.layout")
+
+--- Get the current layout name.
+-- @param screen The screen number.
+function get(screen)
+ local t = tag.selected(screen)
+ if t then
+ return t.layout
+ end
+end
+
+--- Change the layout of the current tag.
+-- @param layouts A table of layouts.
+-- @param i Relative index.
+function inc(layouts, i)
+ local t = tag.selected()
+ local number_of_layouts = 0
+ local rev_layouts = {}
+ for i, v in ipairs(layouts) do
+ rev_layouts[v] = i
+ number_of_layouts = number_of_layouts + 1
+ end
+ if t then
+ local cur_layout = get()
+ local new_layout_index = (rev_layouts[cur_layout] + i) % number_of_layouts
+ if new_layout_index == 0 then
+ new_layout_index = number_of_layouts
+ end
+ t.layout = layouts[new_layout_index]
+ end
+end
+
+--- Set the layout of the current tag by name.
+-- @param layout Layout name.
+function set(layout)
+ local t = tag.selected()
+ if t then
+ t.layout = layout
+ end
+end
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
diff --git a/lib/awful/placement.lua.in b/lib/awful/placement.lua.in
new file mode 100644
index 000000000..95958a138
--- /dev/null
+++ b/lib/awful/placement.lua.in
@@ -0,0 +1,175 @@
+---------------------------------------------------------------------------
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @release @AWESOME_VERSION@
+---------------------------------------------------------------------------
+
+-- Grab environment we need
+local ipairs = ipairs
+local pairs = pairs
+local math = math
+local table = table
+local capi =
+{
+ screen = screen,
+ mouse = mouse,
+}
+local client = require("awful.client")
+local layout = require("awful.layout")
+
+--- Placement module for awful
+module("awful.placement")
+
+--- Check if an area intersect another area.
+-- @param a The area.
+-- @param b The other area.
+-- @return True if they intersect, false otherwise.
+local function area_intersect_area(a, b)
+ return (b.x < a.x + a.width
+ and b.x + b.width > a.x
+ and b.y < a.y + a.height
+ and b.y + b.height > a.y)
+end
+
+--- Get the intersect area between a and b.
+-- @param a The area.
+-- @param b The other area.
+-- @return The intersect area.
+local function area_intersect_area_get(a, b)
+ local g = {}
+ g.x = math.max(a.x, b.x)
+ g.y = math.max(a.y, b.y)
+ g.width = math.min(a.x + a.width, b.x + b.width) - g.x
+ g.height = math.min(a.y + a.height, b.y + b.height) - g.y
+ return g
+end
+
+--- Remove an area from a list, splitting the space between several area that
+-- can overlap.
+-- @param areas Table of areas.
+-- @param elem Area to remove.
+-- @return The new area list.
+local function area_remove(areas, elem)
+ local newareas = areas
+ for i, r in ipairs(areas) do
+ -- Check if the 'elem' intersect
+ if area_intersect_area(r, elem) then
+ -- It does? remove it
+ table.remove(areas, i)
+ local inter = area_intersect_area_get(r, elem)
+
+ if inter.x > r.x then
+ table.insert(newareas, {
+ x = r.x,
+ y = r.y,
+ width = inter.x - r.x,
+ height = r.height
+ })
+ end
+
+ if inter.y > r.y then
+ table.insert(newareas, {
+ x = r.x,
+ y = r.y,
+ width = r.width,
+ height = inter.y - r.y
+ })
+ end
+
+ if inter.x + inter.width < r.x + r.width then
+ table.insert(newareas, {
+ x = inter.x + inter.width,
+ y = r.y,
+ width = (r.x + r.width) - (inter.x + inter.width),
+ height = r.height
+ })
+ end
+
+ if inter.y + inter.height < r.y + r.height then
+ table.insert(newareas, {
+ x = r.x,
+ y = inter.y + inter.height,
+ width = r.width,
+ height = (r.y + r.height) - (inter.y + inter.height)
+ })
+ end
+ end
+ end
+
+ return newareas
+end
+
+--- Place the client without it being outside the screen.
+-- @param c The client.
+function no_offscreen(c)
+ local geometry = c:fullcoords()
+ local screen_geometry = capi.screen[c.screen].workarea
+
+ if geometry.x + geometry.width > screen_geometry.x + screen_geometry.width then
+ geometry.x = screen_geometry.x + screen_geometry.width - geometry.width
+ elseif geometry.x < screen_geometry.x then
+ geometry.x = screen_geometry.x
+ end
+
+ if geometry.y + geometry.height > screen_geometry.y + screen_geometry.height then
+ geometry.y = screen_geometry.y + screen_geometry.height - geometry.height
+ elseif geometry.y < screen_geometry.y then
+ geometry.y = screen_geometry.y
+ end
+
+ c:fullcoords(geometry)
+end
+
+--- Place the client where there's place available with minimum overlap.
+-- @param c The client.
+function no_overlap(c)
+ local cls = client.visible(c.screen)
+ local layout = layout.get()
+ local areas = { capi.screen[c.screen].workarea }
+ local coords = c:coords()
+ local fullcoords = c:fullcoords()
+ for i, cl in pairs(cls) do
+ if cl ~= c and (cl.floating or layout == "floating") then
+ areas = area_remove(areas, cl:fullcoords())
+ end
+ end
+
+ -- Look for available space
+ local found = false
+ local new = { x = coords.x, y = coords.y, width = 0, height = 0 }
+ for i, r in ipairs(areas) do
+ if r.width >= fullcoords.width
+ and r.height >= fullcoords.height
+ and r.width * r.height > new.width * new.height then
+ found = true
+ new = r
+ end
+ end
+
+ -- We did not foudn an area with enough space for our size:
+ -- just take the biggest available one and go in
+ if not found then
+ for i, r in ipairs(areas) do
+ if r.width * r.height > new.width * new.height then
+ new = r
+ end
+ end
+ end
+
+ -- Restore height and width
+ new.width = coords.width
+ new.height = coords.height
+
+ c:coords(new)
+end
+
+--- Place the client under the mouse.
+-- @param c The client.
+function under_mouse(c)
+ local c_coords = c:coords()
+ local m_coords = capi.mouse.coords()
+ c:coords({ x = m_coords.x - c_coords.width / 2,
+ y = m_coords.y - c_coords.height / 2 })
+end
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
diff --git a/lib/awful/prompt.lua.in b/lib/awful/prompt.lua.in
new file mode 100644
index 000000000..4e10b0869
--- /dev/null
+++ b/lib/awful/prompt.lua.in
@@ -0,0 +1,305 @@
+---------------------------------------------------------------------------
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @release @AWESOME_VERSION@
+---------------------------------------------------------------------------
+
+-- Grab environment we need
+local assert = assert
+local io = io
+local table = table
+local capi =
+{
+ keygrabber = keygrabber
+}
+local util = require("awful.util")
+local beautiful = require("awful.beautiful")
+
+--- Prompt module for awful
+module("awful.prompt")
+
+--- Private data
+local data = {}
+data.history = {}
+
+--- Load history file in history table
+-- @param id The data.history identifier which is the path to the filename
+-- @param max Optional parameter, the maximum number of entries in file
+local function history_check_load(id, max)
+ if id and id ~= ""
+ and not data.history[id] then
+ data.history[id] = { max = 50, table = {} }
+
+ if max then
+ data.history[id].max = max
+ end
+
+ local f = io.open(id, "r")
+
+ -- Read history file
+ if f then
+ for line in f:lines() do
+ table.insert(data.history[id].table, line)
+ if #data.history[id].table >= data.history[id].max then
+ break
+ end
+ end
+ end
+ end
+end
+
+--- Save history table in history file
+-- @param id The data.history identifier
+local function history_save(id)
+ if data.history[id] then
+ local f = io.open(id, "w")
+ if not f then
+ local i = 0
+ for d in id:gmatch(".-/") do
+ i = i + #d
+ end
+ util.mkdir(id:sub(1, i - 1))
+ f = assert(io.open(id, "w"))
+ end
+ for i = 1, math.min(#data.history[id].table, data.history[id].max) do
+ f:write(data.history[id].table[i] .. "\n")
+ end
+ f:close()
+ end
+end
+
+--- Return the number of items in history table regarding the id
+-- @param id The data.history identifier
+-- @return the number of items in history table, -1 if history is disabled
+local function history_items(id)
+ if data.history[id] then
+ return #data.history[id].table
+ else
+ return -1
+ end
+end
+
+--- Add an entry to the history file
+-- @param id The data.history identifier
+-- @param command The command to add
+local function history_add(id, command)
+ if data.history[id] then
+ if command ~= ""
+ and command ~= data.history[id].table[#data.history[id].table] then
+ table.insert(data.history[id].table, command)
+
+ -- Do not exceed our max_cmd
+ if #data.history[id].table > data.history[id].max then
+ table.remove(data.history[id].table, 1)
+ end
+
+ history_save(id)
+ end
+ end
+end
+
+
+--- Draw the prompt text with a cursor.
+-- @param text The text.
+-- @param text_color The text color.
+-- @param cursor_color The cursor color.
+-- @param cursor_pos The cursor position.
+local function prompt_text_with_cursor(text, text_color, cursor_color, cursor_pos)
+ local char
+ if not text then text = "" end
+ if #text < cursor_pos then
+ char = " "
+ else
+ char = util.escape(text:sub(cursor_pos, cursor_pos))
+ end
+ local text_start = util.escape(text:sub(1, cursor_pos - 1))
+ local text_end = util.escape(text:sub(cursor_pos + 1))
+ return text_start .. "" .. char .. "" .. text_end
+end
+
+--- Run a prompt in a box.
+-- @param args A table with optional arguments: fg_cursor, bg_cursor, prompt.
+-- @param textbox The textbox to use for the prompt.
+-- @param exe_callback The callback function to call with command as argument when finished.
+-- @param completion_callback The callback function to call to get completion.
+-- @param history_path Optional parameter: file path where the history should be saved, set nil to disable history
+-- @param history_max Optional parameter: set the maximum entries in history file, 50 by default
+-- @param done_callback Optional parameter: the callback function to always call without arguments, regardless of whether the prompt was cancelled.
+function run(args, textbox, exe_callback, completion_callback, history_path, history_max, done_callback)
+ local theme = beautiful.get()
+ if not args then args = {} end
+ local command = ""
+ local command_before_comp
+ local cur_pos_before_comp
+ local prettyprompt = args.prompt or ""
+ local inv_col = args.fg_cursor or theme.fg_focus or "black"
+ local cur_col = args.bg_cursor or theme.bg_focus or "white"
+
+ history_check_load(history_path, history_max)
+ local history_index = history_items(history_path) + 1
+ -- The cursor position
+ local cur_pos = 1
+ -- The completion element to use on completion request.
+ local ncomp = 1
+ if not textbox or not exe_callback then
+ return
+ end
+ textbox.text = prettyprompt .. prompt_text_with_cursor(text, inv_col, cur_col, cur_pos)
+ capi.keygrabber.run(
+ function (mod, key)
+ -- Get out cases
+ if mod.Control then
+ if key == "c" or key == "g" then
+ textbox.text = ""
+ if done_callback then done_callback() end
+ return false
+ elseif key == "j" or key == "m" then
+ textbox.text = ""
+ exec_callback(command)
+ if done_callback then done_callback() end
+ return false
+ end
+ else
+ if key == "Return" then
+ textbox.text = ""
+ data.history_add(history_path, command)
+ exe_callback(command)
+ if done_callback then done_callback() end
+ return false
+ elseif key == "Escape" then
+ textbox.text = ""
+ if done_callback then done_callback() end
+ return false
+ end
+ end
+
+ -- Control cases
+ if mod.Control then
+ if key == "a" then
+ cur_pos = 1
+ elseif key == "b" then
+ if cur_pos > 1 then
+ cur_pos = cur_pos - 1
+ end
+ elseif key == "d" then
+ if cur_pos <= #command then
+ command = command:sub(1, cur_pos - 1) .. command:sub(cur_pos + 1)
+ end
+ elseif key == "e" then
+ cur_pos = #command + 1
+ elseif key == "f" then
+ if cur_pos <= #command then
+ cur_pos = cur_pos + 1
+ end
+ elseif key == "h" then
+ if cur_pos > 1 then
+ command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
+ cur_pos = cur_pos - 1
+ end
+ elseif key == "k" then
+ command = command:sub(1, cur_pos - 1)
+ elseif key == "u" then
+ command = command:sub(cur_pos, #command)
+ cur_pos = 1
+ elseif key == "w" then
+ local wstart = 1
+ local wend = 1
+ local cword_start = 1
+ local cword_end = 1
+ while wend < cur_pos do
+ wend = command:find(" ", wstart)
+ if not wend then wend = #command + 1 end
+ if cur_pos >= wstart and cur_pos <= wend + 1 then
+ cword_start = wstart
+ cword_end = cur_pos - 1
+ break
+ end
+ wstart = wend + 1
+ end
+ command = command:sub(1, cword_start - 1) .. command:sub(cword_end + 1)
+ cur_pos = cword_start
+ end
+ else
+ if completion_callback then
+ -- That's tab
+ if key:byte() == 9 or key == "ISO_Left_Tab" then
+ if key == "ISO_Left_Tab" then
+ if ncomp == 1 then return true end
+ if ncomp == 2 then
+ command = command_before_comp
+ textbox.text = prettyprompt .. prompt_text_with_cursor(command_before_comp, inv_col, cur_col, cur_pos)
+ return true
+ end
+
+ ncomp = ncomp - 2
+ elseif ncomp == 1 then
+ command_before_comp = command
+ cur_pos_before_comp = cur_pos
+ end
+ command, cur_pos = completion_callback(command_before_comp, cur_pos_before_comp, ncomp)
+ ncomp = ncomp + 1
+ key = ""
+ else
+ ncomp = 1
+ end
+ end
+
+ -- Typin cases
+ if key == "Home" then
+ cur_pos = 1
+ elseif key == "End" then
+ cur_pos = #command + 1
+ elseif key == "BackSpace" then
+ if cur_pos > 1 then
+ command = command:sub(1, cur_pos - 2) .. command:sub(cur_pos)
+ cur_pos = cur_pos - 1
+ end
+ -- That's DEL
+ elseif key:byte() == 127 then
+ command = command:sub(1, cur_pos - 1) .. command:sub(cur_pos + 1)
+ elseif key == "Left" then
+ cur_pos = cur_pos - 1
+ elseif key == "Right" then
+ cur_pos = cur_pos + 1
+ elseif key == "Up" then
+ if history_index > 1 then
+ history_index = history_index - 1
+
+ command = data.history[history_path].table[history_index]
+ cur_pos = #command + 2
+ end
+ elseif key == "Down" then
+ if history_index < history_items(history_path) then
+ history_index = history_index + 1
+
+ command = data.history[history_path].table[history_index]
+ cur_pos = #command + 2
+ elseif history_index == history_items(history_path) then
+ history_index = history_index + 1
+
+ command = ""
+ cur_pos = 1
+ end
+ else
+ -- len() is UTF-8 aware but #key is not,
+ -- so check that we have one UTF-8 char but advance the cursor of # position
+ if key:len() == 1 then
+ command = command:sub(1, cur_pos - 1) .. key .. command:sub(cur_pos)
+ cur_pos = cur_pos + #key
+ end
+ end
+ if cur_pos < 1 then
+ cur_pos = 1
+ elseif cur_pos > #command + 1 then
+ cur_pos = #command + 1
+ end
+ end
+
+ -- Update textbox
+ textbox.text = prettyprompt .. prompt_text_with_cursor(command, inv_col, cur_col, cur_pos)
+
+ return true
+ end)
+end
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
diff --git a/lib/awful/screen.lua.in b/lib/awful/screen.lua.in
new file mode 100644
index 000000000..24729598c
--- /dev/null
+++ b/lib/awful/screen.lua.in
@@ -0,0 +1,30 @@
+---------------------------------------------------------------------------
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @release @AWESOME_VERSION@
+---------------------------------------------------------------------------
+
+-- Grab environment we need
+local capi =
+{
+ mouse = mouse,
+ screen = screen,
+ client = client
+}
+local util = require("awful.util")
+local client = require("awful.client")
+
+--- Screen module for awful
+module("awful.screen")
+
+--- Give the focus to a screen, and move pointer.
+-- @param Screen number.
+function focus(i)
+ local s = util.cycle(capi.screen.count(), capi.mouse.screen + i)
+ local c = client.focus.history.get(s, 0)
+ if c then capi.client.focus = c end
+ -- Move the mouse on the screen
+ capi.mouse.screen = s
+end
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
diff --git a/lib/awful/tag.lua.in b/lib/awful/tag.lua.in
new file mode 100644
index 000000000..18e118ebc
--- /dev/null
+++ b/lib/awful/tag.lua.in
@@ -0,0 +1,206 @@
+---------------------------------------------------------------------------
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @release @AWESOME_VERSION@
+---------------------------------------------------------------------------
+
+-- Grab environment we need
+local hooks = require("awful.hooks")
+local util = require("awful.util")
+local pairs = pairs
+local ipairs = ipairs
+local capi =
+{
+ screen = screen,
+ mouse = mouse
+}
+
+--- Tag module for awful
+module("awful.tag")
+
+-- Private data
+local data = {}
+data.history = {}
+data.history.past = {}
+data.history.current = {}
+
+-- History functions
+history = {}
+
+--- Compare 2 tables of tags.
+-- @param a The first table.
+-- @param b The second table of tags.
+-- @return True if the tables are identical, false otherwise.
+local function compare_select(a, b)
+ if not a or not b then
+ return false
+ end
+ -- Quick size comparison
+ if #a ~= #b then
+ return false
+ end
+ for ka, va in pairs(a) do
+ if b[ka] ~= va.selected then
+ return false
+ end
+ end
+ for kb, vb in pairs(b) do
+ if a[kb].selected ~= vb then
+ return false
+ end
+ end
+ return true
+end
+
+--- Update the tag history.
+-- @param screen The screen number.
+function history.update(screen)
+ local curtags = capi.screen[screen]:tags()
+ if not compare_select(curtags, data.history.current[screen]) then
+ data.history.past[screen] = data.history.current[screen]
+ data.history.current[screen] = {}
+ for k, v in ipairs(curtags) do
+ data.history.current[screen][k] = v.selected
+ end
+ end
+end
+
+-- Revert tag history.
+-- @param screen The screen number.
+function history.restore(screen)
+ local s = screen or capi.mouse.screen
+ local tags = capi.screen[s]:tags()
+ for k, t in pairs(tags) do
+ t.selected = data.history.past[s][k]
+ end
+end
+
+--- Return a table with all visible tags
+-- @param s Screen number.
+-- @return A table with all selected tags.
+function selectedlist(s)
+ local screen = s or capi.mouse.screen
+ local tags = capi.screen[screen]:tags()
+ local vtags = {}
+ for i, t in pairs(tags) do
+ if t.selected then
+ vtags[#vtags + 1] = t
+ end
+ end
+ return vtags
+end
+
+--- Return only the first visible tag.
+-- @param s Screen number.
+function selected(s)
+ return selectedlist(s)[1]
+end
+
+--- Set master width factor.
+-- @param mwfact Master width factor.
+function setmwfact(mwfact)
+ local t = selected()
+ if t then
+ t.mwfact = mwfact
+ end
+end
+
+--- Increase master width factor.
+-- @param add Value to add to master width factor.
+function incmwfact(add)
+ local t = selected()
+ if t then
+ t.mwfact = t.mwfact + add
+ end
+end
+
+--- Set the number of master windows.
+-- @param nmaster The number of master windows.
+function setnmaster(nmaster)
+ local t = selected()
+ if t then
+ t.nmaster = nmaster
+ end
+end
+
+--- Increase the number of master windows.
+-- @param add Value to add to number of master windows.
+function incnmaster(add)
+ local t = selected()
+ if t then
+ t.nmaster = t.nmaster + add
+ end
+end
+
+--- Set number of column windows.
+-- @param ncol The number of column.
+function setncol(ncol)
+ local t = selected()
+ if t then
+ t.ncol = ncol
+ end
+end
+
+--- Increase number of column windows.
+-- @param add Value to add to number of column windows.
+function incncol(add)
+ local t = selected()
+ if t then
+ t.ncol = t.ncol + add
+ end
+end
+
+--- View no tag.
+-- @param Optional screen number.
+function viewnone(screen)
+ local tags = capi.screen[screen or capi.mouse.screen]:tags()
+ for i, t in pairs(tags) do
+ t.selected = false
+ end
+end
+
+--- View a tag by its index.
+-- @param i The relative index to see.
+-- @param screen Optional screen number.
+function viewidx(i, screen)
+ local tags = capi.screen[screen or capi.mouse.screen]:tags()
+ local sel = selected()
+ viewnone()
+ for k, t in ipairs(tags) do
+ if t == sel then
+ tags[util.cycle(#tags, k + i)].selected = true
+ end
+ end
+end
+
+--- View next tag. This is the same as tag.viewidx(1).
+function viewnext()
+ return viewidx(1)
+end
+
+--- View previous tag. This is the same a tag.viewidx(-1).
+function viewprev()
+ return viewidx(-1)
+end
+
+--- View only a tag.
+-- @param t The tag object.
+function viewonly(t)
+ viewnone(t.screen)
+ t.selected = true
+end
+
+--- View only a set of tags.
+-- @param tags A table with tags to view only.
+-- @param screen Optional screen number of the tags.
+function viewmore(tags, screen)
+ viewnone(screen)
+ for i, t in pairs(tags) do
+ t.selected = true
+ end
+end
+
+-- Register standards hooks
+hooks.arrange.register(history.update)
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
diff --git a/lib/awful/titlebar.lua.in b/lib/awful/titlebar.lua.in
new file mode 100644
index 000000000..090f3913e
--- /dev/null
+++ b/lib/awful/titlebar.lua.in
@@ -0,0 +1,163 @@
+---------------------------------------------------------------------------
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @release @AWESOME_VERSION@
+---------------------------------------------------------------------------
+
+-- Grab environment we need
+local math = math
+local ipairs = ipairs
+local otable = otable
+local capi =
+{
+ wibox = wibox,
+ widget = widget,
+ button = button,
+ client = client,
+}
+local hooks = require("awful.hooks")
+local util = require("awful.util")
+
+--- Titlebar module for awful
+module("awful.titlebar")
+
+-- Privata data
+local data = otable()
+
+--- Create a standard titlebar.
+-- @param c The client.
+-- @param args Arguments.
+-- fg: the foreground color.
+-- bg: the background color.
+-- fg_focus: the foreground color for focused window.
+-- fg_focus: the background color for focused window.
+-- width: the titlebar width
+function add(c, args)
+ if not c or c.type ~= "normal" then return end
+ if not args then args = {} end
+ -- Store colors
+ data[c] = {}
+ data[c].fg = args.fg or theme.titlebar_fg_normal or theme.fg_normal
+ data[c].bg = args.bg or theme.titlebar_bg_normal or theme.bg_normal
+ data[c].fg_focus = args.fg_focus or theme.titlebar_fg_focus or theme.fg_focus
+ data[c].bg_focus = args.bg_focus or theme.titlebar_bg_focus or theme.bg_focus
+ data[c].width = args.width
+
+ -- Built args
+ local targs = {}
+ if args.fg then targs.fg = args.fg end
+ if args.bg then targs.bg = args.bg end
+ local tb = capi.wibox(targs)
+
+ local title = capi.widget({ type = "textbox", name = "title", align = "flex" })
+ title.text = " " .. util.escape(c.name) .. " "
+ local bts =
+ {
+ capi.button({ }, 1, function (t) t.client:mouse_move() end),
+ capi.button({ args.modkey }, 3, function (t) t.client:mouse_resize() end)
+ }
+ title:buttons(bts)
+
+ local appicon = capi.widget({ type = "imagebox", name = "appicon", align = "left" })
+ appicon.image = c.icon
+
+ if theme.titlebar_close_button == "true" then
+ local closef = widget.button({ name = "closef", align = "right",
+ image = theme.titlebar_close_button_focus
+ or theme.titlebar_close_button_img_focus
+ or "@AWESOME_ICON_PATH@/titlebar/closer.png" })
+ local close = widget.button({ name = "close", align = "right",
+ image = theme.titlebar_close_button_normal
+ or theme.titlebar_close_button_img_normal
+ or "@AWESOME_ICON_PATH@/titlebar/close.png" })
+
+ -- Bind kill button
+ local b = capi.button({ }, 1, nil, function (t) t.client:kill() end)
+ local bts = closef:buttons()
+ bts[#bts + 1] = b
+ closef:buttons(bts)
+
+ bts = close:buttons()
+ bts[#bts + 1] = b
+ close:buttons(bts)
+
+ tb:widgets({
+ appicon,
+ title,
+ closef, close
+ })
+ else
+ tb:widgets({
+ appicon,
+ title
+ })
+ end
+
+ c.titlebar = tb
+
+ update(c)
+ update(c, "geometry")
+end
+
+--- Update a titlebar. This should be called in some hooks.
+-- @param c The client to update.
+-- @param prop The property name which has changed.
+function update(c, prop)
+ if c.titlebar and data[c] then
+ local widgets = c.titlebar:widgets()
+ local title, close, closef
+ for k, v in ipairs(widgets) do
+ if v.name == "title" then title = v
+ elseif v.name == "close" then close = v
+ elseif v.name == "closef" then closef = v
+ elseif v.name == "appicon" then appicon = v end
+ if title and close and closef and appicon then break end
+ end
+ if prop == "name" then
+ if title then
+ title.text = " " .. util.escape(c.name) .. " "
+ end
+ elseif prop == "icon" then
+ if appicon then
+ appicon.image = c.icon
+ end
+ elseif prop == "geometry" then
+ if data[c].width then
+ if c.titlebar.position == "top"
+ or c.titlebar.position == "bottom" then
+ local w = math.min(data[c].width, c:coords().width + 2 * c.border_width)
+ c.titlebar:geometry({ width = w })
+ else
+ local w = math.min(data[c].width, c:coords().height + 2 * c.border_width)
+ c.titlebar:geometry({ height = w })
+ end
+ end
+ end
+ if capi.client.focus == c then
+ c.titlebar.fg = data[c].fg_focus
+ c.titlebar.bg = data[c].bg_focus
+ if closef then closef.visible = true end
+ if close then close.visible = false end
+ else
+ c.titlebar.fg = data[c].fg
+ c.titlebar.bg = data[c].bg
+ if closef then closef.visible = false end
+ if close then close.visible = true end
+ end
+ end
+end
+
+--- Remove a titlebar from a client.
+-- @param c The client.
+function remove(c)
+ c.titlebar = nil
+ data[c] = nil
+end
+
+-- Register standards hooks
+hooks.focus.register(update)
+hooks.unfocus.register(update)
+hooks.property.register(update)
+hooks.unmanage.register(remove)
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
diff --git a/lib/awful/util.lua.in b/lib/awful/util.lua.in
new file mode 100644
index 000000000..be2369bf0
--- /dev/null
+++ b/lib/awful/util.lua.in
@@ -0,0 +1,112 @@
+---------------------------------------------------------------------------
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @release @AWESOME_VERSION@
+---------------------------------------------------------------------------
+
+-- Grab environment we need
+local os = os
+local io = io
+local assert = assert
+local loadstring = loadstring
+local debug = debug
+local print = print
+local capi =
+{
+ awesome = awesome,
+ mouse = mouse
+}
+
+--- Utility module for awful
+module("awful.util")
+
+function deprecate()
+ print("W: awful: function is deprecated")
+ print(debug.traceback())
+end
+
+--- Strip alpha part of color.
+-- @param color The color.
+-- @return The color without alpha channel.
+function color_strip_alpha(color)
+ if color:len() == 9 then
+ color = color:sub(1, 7)
+ end
+ return color
+end
+
+--- Make i cycle.
+-- @param t A length.
+-- @param i An absolute index to fit into #t.
+-- @return The object at new index.
+function cycle(t, i)
+ while i > t do i = i - t end
+ while i < 1 do i = i + t end
+ return i
+end
+
+--- Create a directory
+-- @param dir The directory.
+-- @return mkdir return code
+function mkdir(dir)
+ return os.execute("mkdir -p " .. dir)
+end
+
+--- Spawn a program.
+-- @param cmd The command.
+-- @param screen The screen where to spawn window.
+-- @return The awesome.spawn return value.
+function spawn(cmd, screen)
+ if cmd and cmd ~= "" then
+ return capi.awesome.spawn(cmd .. "&", screen or capi.mouse.screen)
+ end
+end
+
+-- Read a program output and returns its output as a string.
+-- @param cmd The command to run.
+-- @return A string with the program output.
+function pread(cmd)
+ if cmd and cmd ~= "" then
+ local f = io.popen(cmd, 'r')
+ local s = f:read("*all")
+ f:close()
+ return s
+ end
+end
+
+--- Eval Lua code.
+-- @return The return value of Lua code.
+function eval(s)
+ return assert(loadstring("return " .. s))()
+end
+
+--- Escape a string from XML char.
+-- Useful to set raw text in textbox.
+-- @param text Text to escape.
+-- @return Escape text.
+function escape(text)
+ if text then
+ text = text:gsub("&", "&")
+ text = text:gsub("<", "<")
+ text = text:gsub(">", ">")
+ text = text:gsub("'", "'")
+ text = text:gsub("\"", """)
+ end
+ return text
+end
+
+--- Unescape a string from entities.
+-- @param text Text to unescape.
+-- @return Unescaped text.
+function unescape(text)
+ if text then
+ text = text:gsub("&", "&")
+ text = text:gsub("<", "<")
+ text = text:gsub(">", ">")
+ text = text:gsub("'", "'")
+ text = text:gsub(""", "\"")
+ end
+ return text
+end
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
diff --git a/lib/awful/widget.lua.in b/lib/awful/widget.lua.in
new file mode 100644
index 000000000..03fdbddf9
--- /dev/null
+++ b/lib/awful/widget.lua.in
@@ -0,0 +1,255 @@
+---------------------------------------------------------------------------
+-- @author Julien Danjou <julien@danjou.info>
+-- @copyright 2008 Julien Danjou
+-- @release @AWESOME_VERSION@
+---------------------------------------------------------------------------
+
+-- Grab environment we need
+local ipairs = ipairs
+local pairs = pairs
+local capi =
+{
+ screen = screen,
+ client = client,
+ button = button,
+ widget = widget,
+ mouse = mouse
+}
+local util = require("awful.util")
+local beautiful = require("awful.beautiful")
+
+--- Widget module for awful
+module("awful.widget")
+
+-- Various public structures
+taglist = {}
+taglist.label = {}
+tasklist = {}
+tasklist.label = {}
+
+--- Return labels for a taglist widget with all tag from screen.
+-- It returns the tag name and set a special
+-- foreground and background color for selected tags.
+-- @param t The tag.
+-- @param args The arguments table.
+-- bg_focus The background color for selected tag.
+-- fg_focus The foreground color for selected tag.
+-- bg_urgent The background color for urgent tags.
+-- fg_urgent The foreground color for urgent tags.
+-- taglist_squares Optional: set "true" or nil to display the taglist squares.
+-- taglist_squares_sel Optional: an user provided image for selected squares.
+-- taglist_squares_unsel Optional: an user provided image for unselected squares.
+-- @return A string to print.
+function taglist.label.all(t, args)
+ if not args then args = {} end
+ local theme = beautiful.get()
+ local fg_focus = args.fg_focus or theme.taglist_fg_focus or theme.fg_focus
+ local bg_focus = args.bg_focus or theme.taglist_bg_focus or theme.bg_focus
+ local fg_urgent = args.fg_urgent or theme.taglist_fg_urgent or theme.fg_urgent
+ local bg_urgent = args.bg_urgent or theme.taglist_bg_urgent or theme.bg_urgent
+ local taglist_squares = args.taglist_squares or theme.taglist_squares
+ local taglist_squares_sel = args.squares_sel or theme.squares_sel
+ local taglist_squares_unsel = args.squares_unsel or theme.squares_unsel
+ local text
+ local background = ""
+ local sel = capi.client.focus
+ local bg_color = nil
+ local fg_color = nil
+ if t.selected then
+ bg_color = bg_focus
+ fg_color = fg_focus
+ end
+ if sel and sel:tags()[t] then
+ if not taglist_squares or taglist_squares == "true" then
+ if taglist_squares_sel then
+ background = "resize=\"true\" image=\"" .. taglist_squares_sel .. "\""
+ else
+ background = "resize=\"true\" image=\"@AWESOME_ICON_PATH@/taglist/squarefw.png\""
+ end
+ end
+ elseif bg_urgent or fg_urgent then
+ for k, c in pairs(t:clients()) do
+ if not taglist_squares or taglist_squares == "true" then
+ if taglist_squares_unsel then
+ background = "resize=\"true\" image=\"" .. taglist_squares_unsel .. "\""
+ else
+ background = "resize=\"true\" image=\"@AWESOME_ICON_PATH@/taglist/squarew.png\""
+ end
+ end
+ if c.urgent then
+ bg_color = bg_urgent
+ fg_color = fg_urgent
+ break
+ end
+ end
+ end
+ if bg_color and fg_color then
+ text = " "..util.escape(t.name).." "
+ else
+ text = " "..util.escape(t.name).." "
+ end
+ return text
+end
+
+--- Return labels for a taglist widget with all *non empty* tags from screen.
+-- It returns the tag name and set a special
+-- foreground and background color for selected tags.
+-- @param t The tag.
+-- @param args The arguments table.
+-- bg_focus The background color for selected tag.
+-- fg_focus The foreground color for selected tag.
+-- bg_urgent The background color for urgent tags.
+-- fg_urgent The foreground color for urgent tags.
+-- @return A string to print.
+function taglist.label.noempty(t, args)
+ if #t:clients() > 0 or t.selected then
+ if not args then args = {} end
+ local theme = beautiful.get()
+ local fg_focus = args.fg_focus or theme.taglist_fg_focus or theme.fg_focus
+ local bg_focus = args.bg_focus or theme.taglist_bg_focus or theme.bg_focus
+ local fg_urgent = args.fg_urgent or theme.taglist_fg_urgent or theme.fg_urgent
+ local bg_urgent = args.bg_urgent or theme.taglist_bg_urgent or theme.bg_urgent
+ local bg_color = nil
+ local fg_color = nil
+ local text
+
+ if t.selected then
+ bg_color = bg_focus
+ fg_color = fg_focus
+ end
+ if bg_urgent and fg_urgent then
+ for k, c in pairs(t:clients()) do
+ if c.urgent then
+ bg_color = bg_urgent
+ fg_color = fg_urgent
+ break
+ end
+ end
+ end
+ if fg_color and bg_color then
+ text = " " .. util.escape(t.name) .. " "
+ else
+ text = " " .. util.escape(t.name) .. " "
+ end
+ return text
+ end
+end
+
+local function widget_tasklist_label_common(c, args)
+ if not args then args = {} end
+ local theme = beautiful.get()
+ local fg_focus = args.fg_focus or theme.tasklist_fg_focus or theme.fg_focus
+ local bg_focus = args.bg_focus or theme.tasklist_bg_focus or theme.bg_focus
+ local fg_urgent = args.fg_urgent or theme.tasklist_fg_urgent or theme.fg_urgent
+ local bg_urgent = args.bg_urgent or theme.tasklist_bg_urgent or theme.bg_urgent
+ local text = ""
+ local name
+ if c.floating then
+ text = ""
+ end
+ if c.minimized then
+ name = util.escape(c.icon_name) or ""
+ else
+ name = util.escape(c.name) or ""
+ end
+ if capi.client.focus == c then
+ if bg_focus and fg_focus then
+ text = text .. " "..name.." "
+ else
+ text = text .. " "..name.." "
+ end
+ elseif c.urgent and bg_urgent and fg_urgent then
+ text = text .. " "..name.." "
+ else
+ text = text .. " "..name.." "
+ end
+ return text
+end
+
+--- Return labels for a tasklist widget with clients from all tags and screen.
+-- It returns the client name and set a special
+-- foreground and background color for focused client.
+-- It also puts a special icon for floating windows.
+-- @param c The client.
+-- @param screen The screen we are drawing on.
+-- @param args The arguments table.
+-- bg_focus The background color for focused client.
+-- fg_focus The foreground color for focused client.
+-- bg_urgent The background color for urgent clients.
+-- fg_urgent The foreground color for urgent clients.
+-- @return A string to print.
+function tasklist.label.allscreen(c, screen, args)
+ return widget_tasklist_label_common(c, args)
+end
+
+--- Return labels for a tasklist widget with clients from all tags.
+-- It returns the client name and set a special
+-- foreground and background color for focused client.
+-- It also puts a special icon for floating windows.
+-- @param c The client.
+-- @param screen The screen we are drawing on.
+-- @param args The arguments table.
+-- bg_focus The background color for focused client.
+-- fg_focus The foreground color for focused client.
+-- bg_urgent The background color for urgent clients.
+-- fg_urgent The foreground color for urgent clients.
+-- @return A string to print.
+function tasklist.label.alltags(c, screen, args)
+ -- Only print client on the same screen as this widget
+ if c.screen ~= screen then return end
+ return widget_tasklist_label_common(c, args)
+end
+
+--- Return labels for a tasklist widget with clients from currently selected tags.
+-- It returns the client name and set a special
+-- foreground and background color for focused client.
+-- It also puts a special icon for floating windows.
+-- @param c The client.
+-- @param screen The screen we are drawing on.
+-- @param args The arguments table.
+-- bg_focus The background color for focused client.
+-- fg_focus The foreground color for focused client.
+-- bg_urgent The background color for urgent clients.
+-- fg_urgent The foreground color for urgent clients.
+-- @return A string to print.
+function tasklist.label.currenttags(c, screen, args)
+ -- Only print client on the same screen as this widget
+ if c.screen ~= screen then return end
+ for k, t in ipairs(capi.screen[screen]:tags()) do
+ if t.selected and c:tags()[t] then
+ return widget_tasklist_label_common(c, args)
+ end
+ end
+end
+
+--- Create a button widget. When clicked, the image is deplaced to make it like
+-- a real button.
+-- @param args Standard widget table arguments, plus image for the image path.
+-- @return A textbox widget configured as a button.
+function button(args)
+ if not args then return end
+ args.type = "textbox"
+ local w = capi.widget(args)
+ local img_release = ""
+ local img_press = ""
+ w.text = img_release
+ w:buttons({ capi.button({}, 1, function () w.text = img_press end, function () w.text = img_release end) })
+ function w.mouse_leave(s) w.text = img_release end
+ function w.mouse_enter(s) if capi.mouse.coords().buttons[1] then w.text = img_press end end
+ return w
+end
+
+--- Create a button widget which will launch a command.
+-- @param args Standard widget table arguments, plus image for the image path
+-- and command for the command to run on click.
+-- @return A launcher widget.
+function launcher(args)
+ if not args.command then return end
+ local w = button(args)
+ local b = w:buttons()
+ b[#b + 1] = capi.button({}, 1, nil, function () util.spawn(args.command) end)
+ w:buttons(b)
+ return w
+end
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
diff --git a/lib/beautiful.lua.in b/lib/beautiful.lua.in
index 150f06ebe..7afb59c9f 100644
--- a/lib/beautiful.lua.in
+++ b/lib/beautiful.lua.in
@@ -9,7 +9,7 @@
local io = io
local print = print
local setmetatable = setmetatable
-local awful = require("awful")
+local util = require("awful.util")
local package = package
local capi =
{
@@ -69,7 +69,7 @@ function init(path)
if key then
if key == "wallpaper_cmd" then
for s = 1, capi.screen.count() do
- awful.spawn(value, s)
+ util.spawn(value, s)
end
elseif key == "font" then
capi.awesome.font(value)