diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9285124c..789ff98c 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -289,7 +289,12 @@ if(GENERATE_DOC)
file(COPY ${SOURCE_DIR}/docs/ldoc.css DESTINATION ${BUILD_DIR}/docs)
add_custom_target(ldoc ALL
- DEPENDS ${BUILD_DIR}/doc/index.html ${BUILD_DIR}/docs/ldoc.css)
+ DEPENDS
+ ${BUILD_DIR}/doc/index.html
+ ${BUILD_DIR}/docs/ldoc.css
+ generate_awesomerc
+ )
+
if (STRICT_TESTS)
set(ldoc_args --fatalwarnings .)
set(ldoc_desc_suffix " (fatal warnings)")
diff --git a/awesomeConfig.cmake b/awesomeConfig.cmake
index ea1d6057..1d4a4e5e 100644
--- a/awesomeConfig.cmake
+++ b/awesomeConfig.cmake
@@ -354,13 +354,39 @@ add_custom_target(lgi-check-run ALL
# {{{ Generate some aggregated documentation from lua script
-file(MAKE_DIRECTORY ${BUILD_DIR}/script_files/)
+add_custom_target(setup_directories DEPENDS lgi-check-run)
+
+add_custom_command(TARGET setup_directories
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${BUILD_DIR}/script_files/
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${BUILD_DIR}/docs/common/
+ COMMAND ${CMAKE_COMMAND} -E make_directory ${BUILD_DIR}/doc/images/
+ COMMAND ${CMAKE_COMMAND} -E copy ${SOURCE_DIR}/docs/_parser.lua ${BUILD_DIR}/docs/
+)
add_custom_command(
OUTPUT ${BUILD_DIR}/docs/06-appearance.md
COMMAND lua ${SOURCE_DIR}/docs/06-appearance.md.lua
${BUILD_DIR}/docs/06-appearance.md
- DEPENDS lgi-check-run
+ DEPENDS
+ lgi-check-run
+ ${SOURCE_DIR}/docs/06-appearance.md.lua
+ ${SOURCE_DIR}/docs/_parser.lua
+)
+
+add_custom_command(
+ OUTPUT ${BUILD_DIR}/docs/common/rules_index.ldoc
+ COMMAND lua ${SOURCE_DIR}/docs/build_rules_index.lua
+ ${BUILD_DIR}/docs/common/rules_index.ldoc
+
+ # Cheap trick until the ldoc `configure_file` is ported to be a build
+ # step rather than part of cmake.
+ COMMAND ${CMAKE_COMMAND} -E copy ${BUILD_DIR}/docs/common/rules_index.ldoc
+ ${SOURCE_DIR}/docs/common/rules_index.ldoc
+
+ DEPENDS
+ lgi-check-run
+ ${SOURCE_DIR}/docs/build_rules_index.lua
+ ${SOURCE_DIR}/docs/_parser.lua
)
add_custom_command(
@@ -370,6 +396,7 @@ add_custom_command(
${BUILD_DIR}/docs/05-awesomerc.md ${SOURCE_DIR}/awesomerc.lua
${BUILD_DIR}/awesomerc.lua
${BUILD_DIR}/script_files/rc.lua
+ DEPENDS ${SOURCE_DIR}/awesomerc.lua ${SOURCE_DIR}/docs/05-awesomerc.md.lua
)
add_custom_command(
@@ -379,15 +406,20 @@ add_custom_command(
# Create a target for the auto-generated awesomerc.lua and other files
add_custom_target(generate_awesomerc DEPENDS
+ setup_directories
${BUILD_DIR}/awesomerc.lua
${BUILD_DIR}/script_files/theme.lua
${BUILD_DIR}/script_files/rc.lua
${SOURCE_DIR}/awesomerc.lua
${BUILD_DIR}/docs/06-appearance.md
${SOURCE_DIR}/docs/05-awesomerc.md.lua
+ ${SOURCE_DIR}/docs/build_rules_index.lua
+ ${BUILD_DIR}/docs/common/rules_index.ldoc
${SOURCE_DIR}/docs/sample_theme.lua
${SOURCE_DIR}/docs/sample_files.lua
${SOURCE_DIR}/awesomerc.lua
+ ${awesome_c_configure_files}
+ ${awesome_lua_configure_files}
)
diff --git a/docs/06-appearance.md.lua b/docs/06-appearance.md.lua
index 48fe4325..cfb583b9 100644
--- a/docs/06-appearance.md.lua
+++ b/docs/06-appearance.md.lua
@@ -1,174 +1,14 @@
#! /usr/bin/lua
local args = {...}
+local parser = require("docs._parser")
local gio = require("lgi").Gio
local gobject = require("lgi").GObject
local glib = require("lgi").GLib
-local name_attr = gio.FILE_ATTRIBUTE_STANDARD_NAME
-local type_attr = gio.FILE_ATTRIBUTE_STANDARD_TYPE
+local paths = parser.get_all_files("./lib/", "lua", parser.get_all_files("./", "c"))
--- Like pairs(), but iterate over keys in a sorted manner. Does not support
--- modifying the table while iterating.
-local function sorted_pairs(t)
- -- Collect all keys
- local keys = {}
- for k in pairs(t) do
- table.insert(keys, k)
- end
-
- table.sort(keys)
-
- -- return iterator function
- local i = 0
- return function()
- i = i + 1
- if keys[i] then
- return keys[i], t[keys[i]]
- end
- end
-end
-
--- Recursive file scanner
-local function get_all_files(path, ext, ret)
- ret = ret or {}
- local enumerator = gio.File.new_for_path(path):enumerate_children(
- table.concat({name_attr, type_attr}, ",") , 0, nil, nil
- )
-
- for file in function() return enumerator:next_file() end do
- local file_name = file:get_attribute_as_string(name_attr)
- local file_type = file:get_file_type()
- if file_type == "REGULAR" and file_name:match(ext or "") then
- table.insert(ret, enumerator:get_child(file):get_path())
- elseif file_type == "DIRECTORY" then
- get_all_files(enumerator:get_child(file):get_path(), ext, ret)
- end
- end
-
- return ret
-end
-
-local function path_to_module(path)
- for _, module in ipairs {
- "awful", "wibox", "gears", "naughty", "menubar", "beautiful"
- } do
- local match = path:match("/"..module.."/([^.]+).lua")
- if match then
- return module.."."..match:gsub("/",".")
- end
- end
-
- error("Cannot figure out module for " .. tostring(path))
-end
-
-local function path_to_html(path)
- local mod = path_to_module(path):gsub(".init", "")
- local f = assert(io.open(path))
- while true do
- local line = f:read()
- if not line then break end
- if line:match("@classmod") then
- f:close()
- return "../classes/".. mod ..".html"
- end
- if line:match("@module") or line:match("@submodule") then
- f:close()
- return "../libraries/".. mod ..".html"
- end
- end
- f:close()
-
- error("Cannot figure out if module or class: " .. tostring(path))
-end
-
-local function get_link(file, element)
- return table.concat {
- "",
- element:match("[. ](.+)"),
- ""
- }
-end
-
-local all_files = get_all_files("./lib/", "lua")
-table.sort(all_files)
-
-local beautiful_vars = {}
-
--- Find all @beautiful doc entries
-for _,file in ipairs(all_files) do
- local f = io.open(file)
-
- local buffer = ""
-
- for line in f:lines() do
-
- local var = line:gmatch("--[ ]*@beautiful ([^ \n]*)")()
-
- -- There is no backward/forward pattern in lua
- if #line <= 1 then
- buffer = ""
- elseif #buffer and not var then
- buffer = buffer.."\n"..line
- elseif line:sub(1,3) == "---" then
- buffer = line
- end
-
-
- if var then
- -- Get the @param, @see and @usage
- local params = ""
- for line in f:lines() do
- if line:sub(1,2) ~= "--" then
- break
- else
- params = params.."\n"..line
- end
- end
-
- local name = var:gmatch("[ ]*beautiful.(.+)")()
- if not name then
- print("WARNING:", var,
- "seems to be misformatted. Use `beautiful.namespace_name`"
- )
- else
- table.insert(beautiful_vars, {
- file = file,
- name = name,
- link = get_link(file, var),
- desc = buffer:gmatch("[- ]+([^\n.]*)")() or "",
- mod = path_to_module(file),
- })
- end
-
- buffer = ""
- end
- end
-end
-
-local function create_table(entries, columns)
- local lines = {}
-
- for _, entry in ipairs(entries) do
- local line = "
"
-
- for _, column in ipairs(columns) do
- line = line..""..entry[column].." | "
- end
-
- table.insert(lines, line.."
\n")
- end
-
- return [[
\n"
-end
+local beautiful_vars = parser.parse_files(paths, "beautiful")
local override_cats = {
["border" ] = true,
@@ -200,7 +40,7 @@ local function create_sample(entries)
" local theme = {}"
}
- for name, cat in sorted_pairs(categorize(entries)) do
+ for name, cat in parser.sorted_pairs(categorize(entries)) do
table.insert(ret, "\n -- "..name)
for _, v in ipairs(cat) do
table.insert(ret, " -- theme."..v.name.." = nil")
@@ -217,6 +57,7 @@ local function create_sample(entries)
return table.concat(ret, '\n')
end
+
-- Create the file
local filename = args[1]
@@ -229,8 +70,9 @@ f:write[[
Beautiful is where Awesome theme variables are stored.
+
]]
-f:write(create_table(beautiful_vars, {"link", "desc"}))
+f:write(parser.create_table(beautiful_vars, {"link", "desc"}))
f:write("\n\n## Sample theme file\n\n")
@@ -241,6 +83,5 @@ f:close()
--TODO add some linting to direct undeclared beautiful variables
--TODO re-generate all official themes
--TODO generate a complete sample theme
---TODO also parse C files.
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/docs/_parser.lua b/docs/_parser.lua
new file mode 100644
index 00000000..323f8677
--- /dev/null
+++ b/docs/_parser.lua
@@ -0,0 +1,191 @@
+local gio = require("lgi").Gio
+local gobject = require("lgi").GObject
+local glib = require("lgi").GLib
+
+local name_attr = gio.FILE_ATTRIBUTE_STANDARD_NAME
+local type_attr = gio.FILE_ATTRIBUTE_STANDARD_TYPE
+
+local module = {}
+
+-- Like pairs(), but iterate over keys in a sorted manner. Does not support
+-- modifying the table while iterating.
+local function sorted_pairs(t)
+ -- Collect all keys
+ local keys = {}
+ for k in pairs(t) do
+ table.insert(keys, k)
+ end
+
+ table.sort(keys)
+
+ -- return iterator function
+ local i = 0
+ return function()
+ i = i + 1
+ if keys[i] then
+ return keys[i], t[keys[i]]
+ end
+ end
+end
+
+-- Recursive file scanner
+local function get_all_files(path, ext, ret)
+ ret = ret or {}
+ local enumerator = gio.File.new_for_path(path):enumerate_children(
+ table.concat({name_attr, type_attr}, ",") , 0, nil, nil
+ )
+
+ for file in function() return enumerator:next_file() end do
+ local file_name = file:get_attribute_as_string(name_attr)
+ local file_type = file:get_file_type()
+ local match_ext = file_name:match("[.]"..ext.."$" or "")
+ local fpath = enumerator:get_child(file):get_path()
+ local is_test = fpath:match("/tests/")
+ if file_type == "REGULAR" and match_ext and not is_test then
+ table.insert(ret, fpath)
+ elseif file_type == "DIRECTORY" then
+ get_all_files(enumerator:get_child(file):get_path(), ext, ret)
+ end
+ end
+
+ return ret
+end
+
+local function path_to_module(path)
+ if path:match("[.]c$") then
+ return path:gmatch("/([^./]+)[.]c$")()
+ end
+
+ for _, module in ipairs {
+ "awful", "wibox", "gears", "naughty", "menubar", "beautiful"
+ } do
+ local match = path:match("/"..module.."/([^.]+).lua")
+ if match then
+ return module.."."..match:gsub("/",".")
+ end
+ end
+
+ error("Cannot figure out module for " .. tostring(path))
+end
+
+function module.path_to_html(path)
+ local mod = path_to_module(path):gsub(".init", "")
+ local f = assert(io.open(path))
+ while true do
+ local line = f:read()
+ if not line then break end
+ if line:match("@classmod") then
+ f:close()
+ return "../classes/".. mod ..".html#"
+ end
+ if line:match("@module") or line:match("@submodule") then
+ f:close()
+ return "../libraries/".. mod ..".html#"
+ end
+ end
+ f:close()
+
+ error("Cannot figure out if module or class: " .. tostring(path))
+end
+
+local function get_link(file, element, element_name)
+ return table.concat {
+ "",
+ element_name,
+ ""
+ }
+end
+
+local function parse_files(paths, property_name, matcher, name_matcher)
+ local exp1 = "[-*]*[ ]*@".. property_name .." ([^ \n]*)"
+ local exp2 = matcher or "[-*]*[ ]*".. property_name ..".(.+)"
+ local exp3 = name_matcher or "[. ](.+)"
+
+ local ret = {}
+
+ table.sort(paths)
+
+ -- Find all @beautiful doc entries
+ for _,file in ipairs(paths) do
+ local f = io.open(file)
+
+ local buffer = ""
+
+ for line in f:lines() do
+
+ local var = line:gmatch(exp1)()
+
+ -- There is no backward/forward pattern in lua
+ if #line <= 1 then
+ buffer = ""
+ elseif #buffer and not var then
+ buffer = buffer.."\n"..line
+ elseif line:sub(1,3) == "---" or line:sub(1,3) == "/**" then
+ buffer = line
+ end
+
+ if var then
+ -- Get the @param, @see and @usage
+ local params = ""
+ for line in f:lines() do
+ if line:sub(1,2) ~= "--" and line:sub(1,2) ~= " *" then
+ break
+ else
+ params = params.."\n"..line
+ end
+ end
+
+ local name = var:gmatch(exp2)()
+ if not name then
+ print("WARNING:", var,
+ "seems to be misformatted. Use `beautiful.namespace_name`"
+ )
+ else
+ table.insert(ret, {
+ file = file,
+ name = name:gsub("_", "\\_"),
+ link = get_link(file, var, var:match(exp3):gsub("_", "\\_")),
+ desc = buffer:gmatch("[-*/ \n]+([^\n.]*)")() or "",
+ mod = path_to_module(file),
+ })
+ end
+
+ buffer = ""
+ end
+ end
+ end
+
+ return ret
+end
+
+local function create_table(entries, columns, prefix)
+ prefix = prefix or ""
+ local lines = {}
+
+ for _, entry in ipairs(entries) do
+ local line = " "
+
+ for _, column in ipairs(columns) do
+ line = line..""..entry[column].." | "
+ end
+
+ table.insert(lines, prefix..line.."
\n")
+ end
+
+ return [[--\n"
+end
+
+module.create_table = create_table
+module.parse_files = parse_files
+module.sorted_pairs = sorted_pairs
+module.get_all_files = get_all_files
+
+return module
diff --git a/docs/build_rules_index.lua b/docs/build_rules_index.lua
new file mode 100644
index 00000000..1e9341c4
--- /dev/null
+++ b/docs/build_rules_index.lua
@@ -0,0 +1,30 @@
+#! /usr/bin/lua
+local args = {...}
+local parser = require("docs._parser")
+
+local files = {"./objects/client.c", "./lib/awful/client.lua"}
+local matcher, matcher2 = "(.*)", ".*"
+
+-- The client function comes from 5 different files, but all of those are
+-- merged into one documentation page (aka, awful.client doesn't have content
+-- anymore). This override the path so the parser doesn't have to be aware of it
+function parser.path_to_html()
+ return "../classes/client.html#client."
+end
+
+local clientruleproperty = parser.parse_files(files, "clientruleproperty", matcher, matcher2)
+
+for _, prop in ipairs(parser.parse_files(files, "property", matcher, matcher2)) do
+ table.insert(clientruleproperty, prop)
+end
+
+-- Create the file
+local filename = args[1]
+
+local f = io.open(filename, "w")
+
+f:write(parser.create_table(clientruleproperty, {"link", "desc"}, "-- "))
+
+f:close()
+
+-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
diff --git a/docs/common/rules_index.ldoc b/docs/common/rules_index.ldoc
new file mode 100644
index 00000000..ac75c08c
--- /dev/null
+++ b/docs/common/rules_index.ldoc
@@ -0,0 +1,72 @@
+--
+--
+-- Name |
+-- Description |
+--
+-- placement | The client default placement on the screen |
+-- honor\_padding | When applying the placement, honor the screen padding |
+-- honor\_workarea | When applying the placement, honor the screen work area |
+-- tag | The client default tag |
+-- tags | The client default tags |
+-- new\_tag | Create a new tag for this client |
+-- switch\_to\_tags | Unselect the current tags and select this client tags |
+-- focus | Define if the client should grab focus by default |
+-- titlebars\_enabled | Should this client have a titlebar by default |
+-- callback | A function to call when this client is ready |
+-- marked | If a client is marked or not |
+-- is\_fixed | Return if a client has a fixed size or not |
+-- immobilized | Is the client immobilized horizontally? |
+-- immobilized | Is the client immobilized vertically? |
+-- floating | The client floating state |
+-- x | The x coordinates |
+-- y | The y coordinates |
+-- width | The width of the client |
+-- height | The height of the client |
+-- dockable | If the client is dockable |
+-- requests\_no\_titlebar | If the client requests not to be decorated with a titlebar |
+-- shape | Set the client shape |
+-- window | The X window id |
+-- name | The client title |
+-- skip\_taskbar | True if the client does not want to be in taskbar |
+-- type | The window type |
+-- class | The client class |
+-- instance | The client instance |
+-- pid | The client PID, if available |
+-- role | The window role, if available |
+-- machine | The machine client is running on |
+-- icon\_name | The client name when iconified |
+-- icon | The client icon as a surface |
+-- icon\_sizes | The available sizes of client icons |
+-- screen | Client screen |
+-- hidden | Define if the client must be hidden, i |
+-- minimized | Define it the client must be iconify, i |
+-- size\_hints\_honor | Honor size hints, e |
+-- border\_width | The client border width |
+-- border\_color | The client border color |
+-- urgent | The client urgent state |
+-- content | A cairo surface for the client window content |
+-- opacity | The client opacity |
+-- ontop | The client is on top of every other windows |
+-- above | The client is above normal windows |
+-- below | The client is below normal windows |
+-- fullscreen | The client is fullscreen or not |
+-- maximized | The client is maximized (horizontally and vertically) or not |
+-- maximized\_horizontal | The client is maximized horizontally or not |
+-- maximized\_vertical | The client is maximized vertically or not |
+-- transient\_for | The client the window is transient for |
+-- group\_window | Window identification unique to a group of windows |
+-- leader\_window | Identification unique to windows spawned by the same command |
+-- size\_hints | A table with size hints of the client |
+-- motif\_wm\_hints | The motif WM hints of the client |
+-- sticky | Set the client sticky, i |
+-- modal | Indicate if the client is modal |
+-- focusable | True if the client can receive the input focus |
+-- shape\_bounding | The client's bounding shape as set by awesome as a (native) cairo surface |
+-- shape\_clip | The client's clip shape as set by awesome as a (native) cairo surface |
+-- shape\_input | The client's input shape as set by awesome as a (native) cairo surface |
+-- client\_shape\_bounding | The client's bounding shape as set by the program as a (native) cairo surface |
+-- client\_shape\_clip | The client's clip shape as set by the program as a (native) cairo surface |
+-- startup\_id | The FreeDesktop StartId |
+-- valid | If the client that this object refers to is still managed by awesome |
+-- first\_tag | The first tag of the client |
+--
diff --git a/docs/config.ld b/docs/config.ld
index a640e4f7..0d783ace 100644
--- a/docs/config.ld
+++ b/docs/config.ld
@@ -72,6 +72,8 @@ new_type("callback", "Callback functions prototype", false, "Parameters")
new_type("rulesources", "Rule sources", false, "param")
-- Filter functions for the taglist/tasklist/layoutlist
new_type("filterfunction", "List filters", false)
+-- Extra client properties available only in awful.rules/spawn constructs
+new_type("clientruleproperty", "Extra properties available in awful.rules and awful.spawn", false, "Type")
-- More fitting section names
kind_names={topic='Documentation', module='Libraries', script='Sample files'}
@@ -135,10 +137,14 @@ file = {
}
}
+local no_prefix = {
+ property = true, signal = true, clientruleproperty = true
+}
+
custom_display_name_handler = function(item, default_handler)
-- Remove the "namespace" from the signals and properties
- if item.type == "property" or item.type == "signal" then
+ if no_prefix[item.type] then
local name = item.name:match("%.([^.]+)$")
return name ~= "" and name or item.name
end
diff --git a/docs/ldoc.css b/docs/ldoc.css
index 6cea44f1..a761088f 100644
--- a/docs/ldoc.css
+++ b/docs/ldoc.css
@@ -22,7 +22,7 @@ hr {
margin: 15px 0;
}
-code, tt {
+tt {
font-family: monospace;
}
span.parameter {
@@ -47,6 +47,19 @@ p.name {
font-family: monospace;
}
+kbd, p code, ol code {
+ background-color: #eaedf587;
+ padding-left: 3px;
+ padding-right: 3px;
+ border-radius: 3px;
+ border-style: solid;
+ border-width: 1px;
+ border-color: #b7bac1;
+ font-family: monospace;
+ margin-left: 5px;
+ margin-right: 5px;
+}
+
#navigation {
float: left;
background-color: white;
@@ -136,6 +149,7 @@ pre {
padding: 15px;
overflow: auto;
font-family: monospace;
+ max-width: 720px;
}
#content ul pre.example {
@@ -191,6 +205,58 @@ table th, table td {
padding: 2px;
}
+.widget_list td {
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+
+.widget_list tr td:first-child {
+ padding-left: 5px;
+}
+
+.widget_list tr td:last-child {
+ padding-right: 10px;
+}
+
+.widget_list {
+ border-collapse: unset;
+ overflow: hidden;
+ border-style: solid;
+ border-width: 0.5px;
+ border-top-left-radius: 7px;
+ border-top-right-radius: 7px;
+}
+
+.widget_list th {
+ background-color: #2c3e67;
+ font-weight: bold;
+ color: white;
+ padding-top: 10px;
+ padding-bottom: 10px;
+}
+
+.widget_list th:first-child {
+ border-top-left-radius: 7px;
+ border-bottom-width: 1px;
+ padding-left: 20px;
+ padding-right: 20px;
+}
+
+.widget_list th:last-child {
+ border-top-right-radius: 7px;
+ border-width: 0px;
+ border-bottom-width: 1px;
+ padding-left: 20px;
+ padding-right: 20px;
+}
+
+.widget_list td {
+ border-style: solid;
+ border-width: 0px;
+ border-right-width: 1px;;
+ border-bottom-width: 1px;
+}
+
#about {
padding: 15px;
padding-left: 16em;
diff --git a/docs/load_ldoc.cmake b/docs/load_ldoc.cmake
index a783e7db..161d49e0 100644
--- a/docs/load_ldoc.cmake
+++ b/docs/load_ldoc.cmake
@@ -1,23 +1,23 @@
# To avoid copy pasting, some documentation is stored in reusable files
set(SHAPE_FILE "${SOURCE_DIR}/docs/common/${SHAPE_NAME}.lua")
-set(path "${SOURCE_DIR}/docs/common/")
+foreach(path ${BUILD_DIR}/docs/common/;${SOURCE_DIR}/docs/common/)
+ # Get the documentation file list
+ file(GLOB doc_files RELATIVE "${path}" "${path}/*.ldoc")
-# Get the documentation file list
-file(GLOB doc_files RELATIVE "${path}" "${path}/*.ldoc")
+ foreach(doc_file_name ${doc_files})
+ # Read the file
+ file(READ "${path}/${doc_file_name}" doc_file_content)
-foreach(doc_file_name ${doc_files})
- # Read the file
- file(READ "${path}/${doc_file_name}" doc_file_content)
+ # Remove the file extension
+ string(REGEX REPLACE "\\.ldoc" "" DOC_FILE_NAME ${doc_file_name})
- # Remove the file extension
- string(REGEX REPLACE "\\.ldoc" "" DOC_FILE_NAME ${doc_file_name})
+ # There is a trailing \n, remove it or it cannot be included in existing blocks
+ string(REGEX REPLACE "\n$" "" doc_file_content "${doc_file_content}")
- # There is a trailing \n, remove it or it cannot be included in existing blocks
- string(REGEX REPLACE "\n$" "" doc_file_content "${doc_file_content}")
-
- # Create a new variable usable from lua files
- set(DOC_${DOC_FILE_NAME}_COMMON "${doc_file_content}")
+ # Create a new variable usable from lua files
+ set(DOC_${DOC_FILE_NAME}_COMMON "${doc_file_content}")
+ endforeach()
endforeach()
# vim: filetype=cmake:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80:foldmethod=marker
diff --git a/lib/awful/client.lua b/lib/awful/client.lua
index ffa20e04..f75270cf 100644
--- a/lib/awful/client.lua
+++ b/lib/awful/client.lua
@@ -59,6 +59,93 @@ client.property = {}
client.shape = require("awful.client.shape")
client.focus = require("awful.client.focus")
+--- The client default placement on the screen.
+--
+-- The default config uses:
+--
+-- awful.placement.no_overlap+awful.placement.no_offscreen
+--
+-- @clientruleproperty placement
+-- @see awful.placement
+
+--- When applying the placement, honor the screen padding.
+-- @clientruleproperty honor_padding
+-- @param[opt=true] boolean
+-- @see awful.placement
+
+--- When applying the placement, honor the screen work area.
+--
+-- The workarea is the part of the screen that excludes the bars and docks.
+--
+-- @clientruleproperty honor_workarea
+-- @param[opt=true] boolean
+-- @see awful.placement
+
+--- The client default tag.
+-- @clientruleproperty tag
+-- @param tag
+-- @see tag
+-- @see new_tag
+-- @see tags
+-- @see switch_to_tags
+
+--- The client default tags.
+--
+-- Avoid using the tag and tags properties at the same time, it will cause
+-- issues.
+--
+-- @clientruleproperty tags
+-- @param[opt={tag}] table
+-- @see tag
+-- @see new_tag
+-- @see tags
+-- @see switch_to_tags
+
+--- Create a new tag for this client.
+--
+-- If the value is `true`, the new tag will be named after the client `class`.
+-- If it is a string, it will be the tag name.
+--
+-- If a table is used, all of its properties will be passed to the tag
+-- constructor:
+--
+-- new_tag = {
+-- name = "My new tag!", -- The tag name.
+-- layout = awful.layout.suit.max, -- Set the tag layout.
+-- volatile = true, -- Remove the tag when the client is closed.
+-- }
+--
+-- @tparam[opt=false] table|string|boolean new_tag
+-- @clientruleproperty new_tag
+-- @see tag
+-- @see tags
+-- @see switch_to_tags
+
+--- Unselect the current tags and select this client tags.
+-- Note that this property was called `switchtotag` in previous Awesome versions.
+-- @clientruleproperty switch_to_tags
+-- @param[opt=false] boolean
+-- @see tag.selected
+
+--- Define if the client should grab focus by default.
+--
+-- The `request::activate` context for this call is `rules`.
+--
+-- @clientruleproperty focus
+-- @param[opt=false] boolean
+
+--- Should this client have a titlebar by default.
+-- @clientruleproperty titlebars_enabled
+-- @param[opt=false] boolean
+-- @see awful.titlebar
+
+--- A function to call when this client is ready.
+--
+-- It can be useful to set extra properties or perform actions.
+--
+-- @clientruleproperty callback
+-- @see awful.spawn
+
--- Jump to the given client.
-- Takes care of focussing the screen, the right tag, etc.
--
diff --git a/lib/awful/rules.lua b/lib/awful/rules.lua
index 29d7e4bf..8e60794c 100644
--- a/lib/awful/rules.lua
+++ b/lib/awful/rules.lua
@@ -8,17 +8,98 @@
-- to add random properties that will be later accessible as `c.property_name`
-- (where `c` is a valid client object)
--
--- In addition to the existing properties, the following are supported:
+-- Syntax
+-- ===
+-- You should fill this table with your rule and properties to apply.
+-- For example, if you want to set xterm maximized at startup, you can add:
--
--- * placement
--- * honor_padding
--- * honor_workarea
--- * tag
--- * new_tag
--- * switch_to_tags (also called switchtotag)
--- * focus
--- * titlebars_enabled
--- * callback
+-- { rule = { class = "xterm" },
+-- properties = { maximized_vertical = true, maximized_horizontal = true } }
+--
+-- If you want to set mplayer floating at startup, you can add:
+--
+-- { rule = { name = "MPlayer" },
+-- properties = { floating = true } }
+--
+-- If you want to put Firefox on a specific tag at startup, you can add:
+--
+-- { rule = { instance = "firefox" },
+-- properties = { tag = mytagobject } }
+--
+-- Alternatively, you can specify the tag by name:
+--
+-- { rule = { instance = "firefox" },
+-- properties = { tag = "3" } }
+--
+-- If you want to put Thunderbird on a specific screen at startup, use:
+--
+-- { rule = { instance = "Thunderbird" },
+-- properties = { screen = 1 } }
+--
+-- Assuming that your X11 server supports the RandR extension, you can also specify
+-- the screen by name:
+--
+-- { rule = { instance = "Thunderbird" },
+-- properties = { screen = "VGA1" } }
+--
+-- If you want to put Emacs on a specific tag at startup, and immediately switch
+-- to that tag you can add:
+--
+-- { rule = { class = "Emacs" },
+-- properties = { tag = mytagobject, switchtotag = true } }
+--
+-- If you want to apply a custom callback to execute when a rule matched,
+-- for example to pause playing music from mpd when you start dosbox, you
+-- can add:
+--
+-- { rule = { class = "dosbox" },
+-- callback = function(c)
+-- awful.spawn('mpc pause')
+-- end }
+--
+-- Note that all "rule" entries need to match. If any of the entry does not
+-- match, the rule won't be applied.
+--
+-- If a client matches multiple rules, they are applied in the order they are
+-- put in this global rules table. If the value of a rule is a string, then the
+-- match function is used to determine if the client matches the rule.
+--
+-- If the value of a property is a function, that function gets called and
+-- function's return value is used for the property.
+--
+-- To match multiple clients to a rule one need to use slightly different
+-- syntax:
+--
+-- { rule_any = { class = { "MPlayer", "Nitrogen" }, instance = { "xterm" } },
+-- properties = { floating = true } }
+--
+-- To match multiple clients with an exception one can couple `rules.except` or
+-- `rules.except_any` with the rules:
+--
+-- { rule = { class = "Firefox" },
+-- except = { instance = "Navigator" },
+-- properties = {floating = true},
+-- },
+--
+-- { rule_any = { class = { "Pidgin", "Xchat" } },
+-- except_any = { role = { "conversation" } },
+-- properties = { tag = "1" }
+-- }
+--
+-- { rule = {},
+-- except_any = { class = { "Firefox", "Vim" } },
+-- properties = { floating = true }
+-- }
+--
+-- Applicable client properties
+-- ===
+--
+-- The table below holds the list of default client properties along with
+-- some extra properties that are specific to the rules. Note that any property
+-- can be set in the rules and interpreted by user provided code. This table
+-- only represent those offered by default.
+--
+--@DOC_rules_index_COMMON@
--
-- @author Julien Danjou <julien@danjou.info>
-- @copyright 2009 Julien Danjou
@@ -44,90 +125,7 @@ local unpack = unpack or table.unpack -- luacheck: globals unpack (compatibility
local rules = {}
---[[--
-This is the global rules table.
-
-You should fill this table with your rule and properties to apply.
-For example, if you want to set xterm maximized at startup, you can add:
-
- { rule = { class = "xterm" },
- properties = { maximized_vertical = true, maximized_horizontal = true } }
-
-If you want to set mplayer floating at startup, you can add:
-
- { rule = { name = "MPlayer" },
- properties = { floating = true } }
-
-If you want to put Firefox on a specific tag at startup, you can add:
-
- { rule = { instance = "firefox" },
- properties = { tag = mytagobject } }
-
-Alternatively, you can specify the tag by name:
-
- { rule = { instance = "firefox" },
- properties = { tag = "3" } }
-
-If you want to put Thunderbird on a specific screen at startup, use:
-
- { rule = { instance = "Thunderbird" },
- properties = { screen = 1 } }
-
-Assuming that your X11 server supports the RandR extension, you can also specify
-the screen by name:
-
- { rule = { instance = "Thunderbird" },
- properties = { screen = "VGA1" } }
-
-If you want to put Emacs on a specific tag at startup, and immediately switch
-to that tag you can add:
-
- { rule = { class = "Emacs" },
- properties = { tag = mytagobject, switch_to_tags = true } }
-
-If you want to apply a custom callback to execute when a rule matched,
-for example to pause playing music from mpd when you start dosbox, you
-can add:
-
- { rule = { class = "dosbox" },
- callback = function(c)
- awful.spawn('mpc pause')
- end }
-
-Note that all "rule" entries need to match. If any of the entry does not
-match, the rule won't be applied.
-
-If a client matches multiple rules, they are applied in the order they are
-put in this global rules table. If the value of a rule is a string, then the
-match function is used to determine if the client matches the rule.
-
-If the value of a property is a function, that function gets called and
-function's return value is used for the property.
-
-To match multiple clients to a rule one need to use slightly different
-syntax:
-
- { rule_any = { class = { "MPlayer", "Nitrogen" }, instance = { "xterm" } },
- properties = { floating = true } }
-
-To match multiple clients with an exception one can couple `rules.except` or
-`rules.except_any` with the rules:
-
- { rule = { class = "Firefox" },
- except = { instance = "Navigator" },
- properties = {floating = true},
- },
-
- { rule_any = { class = { "Pidgin", "Xchat" } },
- except_any = { role = { "conversation" } },
- properties = { tag = "1" }
- }
-
- { rule = {},
- except_any = { class = { "Firefox", "Vim" } },
- properties = { floating = true }
- }
-]]--
+--- This is the global rules table.
rules.rules = {}
--- Check if a client matches a rule.
@@ -536,11 +534,6 @@ function rules.extra_properties.geometry(c, _, props)
c:geometry(new_geo) --TODO use request::geometry
end
---- Create a new tag based on a rule.
--- @tparam client c The client
--- @tparam boolean|function|string value The value.
--- @tparam table props The properties.
--- @treturn tag The new tag
function rules.high_priority_properties.new_tag(c, value, props)
local ty = type(value)
local t = nil
diff --git a/lib/awful/spawn.lua b/lib/awful/spawn.lua
index 402dad5c..6eaff0c0 100644
--- a/lib/awful/spawn.lua
+++ b/lib/awful/spawn.lua
@@ -8,6 +8,9 @@
-- program after it has been launched. This requires currently that the
-- applicaton supports them.
--
+-- Frequently asked questions
+-- ===
+--
-- **Rules of thumb when a shell is needed**:
--
-- * A shell is required when the commands contain `&&`, `;`, `||`, `&` or
@@ -45,44 +48,56 @@
-- *instance*, a *role*, and a *type*. See `client.class`, `client.instance`,
-- `client.role` and `client.type` for more information about these properties.
--
--- **The startup notification protocol**:
+-- **Understanding blocking versus asynchronous execution**:
--
--- The startup notification protocol is an optional specification implemented
--- by X11 applications to bridge the chain of knowledge between the moment a
--- program is launched to the moment its window (client) is shown. It can be
--- found [on the FreeDesktop.org website](https://www.freedesktop.org/wiki/Specifications/startup-notification-spec/).
+-- Awesome is single threaded, it means only one thing is executed at any time.
+-- But Awesome isn't doomed to be slow. It may not have multiple threads, but
+-- it has something called coroutine and also has callbacks. This means things
+-- that take time to execute can still do so in the background (using C threads
+-- or external process + sockets). When they are done, they can notify the
+-- Awesome thread. This works perfectly and avoid blocking Awesome.
--
--- Awesome has support for the various events that are part of the protocol, but
--- the most useful is the identifier, usually identified by its `SNID` acronym in
--- the documentation. It isn't usually necessary to even know it exists, as it
--- is all done automatically. However, if more control is required, the
--- identifier can be specified by an environment variable called
--- `DESKTOP_STARTUP_ID`. For example, let us consider execution of the following
--- command:
+-- If you want to update the text of a `wibox.widget.textbox` with the output
+-- of a shell command, you should use the `awful.spawn.easy_async_with_shell`
+-- command. It is strongly recommanded not to use `io.popen` is explained in the
+-- "Getting a command's output" section. Asynchronous execution is at first a
+-- bit tricky to understand if you never used that before. The example below
+-- should demonstrate how it works.
--
--- DESKTOP_STARTUP_ID="something_TIME$(date '+%s')" my_command
+-- If we do (but *really*, don't do that):
--
--- This should (if the program correctly implements the protocol) result in
--- `c.startup_id` to at least match `something`.
--- This identifier can then be used in `awful.rules` to configure the client.
+-- -- **NEVER, EVER, DO THIS**: your computer will freeze
+-- os.execute("sleep 1; echo foo > /tmp/foo.txt")
+-- mylabel.text = io.popen("cat /tmp/foo.txt"):read("*all")
--
--- Awesome can automatically set the `DESKTOP_STARTUP_ID` variable. This is used
--- by `awful.spawn` to specify additional rules for the startup. For example:
+-- The label will display `foo`. But If we do:
--
--- awful.spawn("urxvt -e maxima -name CALCULATOR", {
--- floating = true,
--- tag = mouse.screen.selected_tag,
--- placement = awful.placement.bottom_right,
--- })
+-- -- Don't do this, it wont work.
+-- -- Assumes /tmp/foo.txt does not exist
+-- awful.spawn.with_shell("sleep 1; echo foo > /tmp/foo.txt")
+-- mylabel.text = io.popen("cat /tmp/foo.txt"):read("*all")
--
--- This can also be used from the command line:
+-- Then the label will be **empty**. `awful.spawn` and `awful.spawn.with_shell`
+-- will **not** block and thus the `io.popen` will be executed before
+-- `sleep 1` finishes. This is why we have async functions to execute shell
+-- commands. There are many variants with difference characteristics and
+-- complexity. `awful.spawn.easy_async` is the most common as it is good enough
+-- for the general "I want to execute a command and do something with the
+-- output when it finishes".
--
--- awesome-client 'awful=require("awful");
--- awful.spawn("urxvt -e maxima -name CALCULATOR", {
--- floating = true,
--- tag = mouse.screen.selected_tag,
--- placement = awful.placement.bottom_right,
--- })'
+-- -- This is the correct way
+-- local command = "sleep 1; echo foo > /tmp/foo.txt"
+--
+-- awful.spawn.easy_async_with_shell(command, function()
+-- awful.spawn.easy_async_with_shell("cat /tmp/foo.txt", function(out)
+-- mylabel.text = out
+-- end)
+-- end)
+--
+-- In this variant, Awesome will not block. Again, like other spawn, you
+-- cannot add code outside of the callback function to use the result of the
+-- command. The code will be executed before the command is finished so the
+-- result wont yet be available.
--
-- **Getting a command's output**:
--
@@ -148,6 +163,53 @@
-- [Desktop Entry](https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html)
-- specification.
--
+-- Spawning applications with specific properties
+-- ===
+--
+-- **The startup notification protocol**:
+--
+-- The startup notification protocol is an optional specification implemented
+-- by X11 applications to bridge the chain of knowledge between the moment a
+-- program is launched to the moment its window (client) is shown. It can be
+-- found [on the FreeDesktop.org website](https://www.freedesktop.org/wiki/Specifications/startup-notification-spec/).
+--
+-- Awesome has support for the various events that are part of the protocol, but
+-- the most useful is the identifier, usually identified by its `SNID` acronym in
+-- the documentation. It isn't usually necessary to even know it exists, as it
+-- is all done automatically. However, if more control is required, the
+-- identifier can be specified by an environment variable called
+-- `DESKTOP_STARTUP_ID`. For example, let us consider execution of the following
+-- command:
+--
+-- DESKTOP_STARTUP_ID="something_TIME$(date '+%s')" my_command
+--
+-- This should (if the program correctly implements the protocol) result in
+-- `c.startup_id` to at least match `something`.
+-- This identifier can then be used in `awful.rules` to configure the client.
+--
+-- Awesome can automatically set the `DESKTOP_STARTUP_ID` variable. This is used
+-- by `awful.spawn` to specify additional rules for the startup. For example:
+--
+-- awful.spawn("urxvt -e maxima -name CALCULATOR", {
+-- floating = true,
+-- tag = mouse.screen.selected_tag,
+-- placement = awful.placement.bottom_right,
+-- })
+--
+-- This can also be used from the command line:
+--
+-- awesome-client 'awful=require("awful");
+-- awful.spawn("urxvt -e maxima -name CALCULATOR", {
+-- floating = true,
+-- tag = mouse.screen.selected_tag,
+-- placement = awful.placement.bottom_right,
+-- })'
+--
+-- This table contains the client properties that are valid when used the
+-- `sn_rules` or `prop` function argument. They are the same as in `awful.rules`.
+--
+--@DOC_rules_index_COMMON@
+--
-- @author Julien Danjou <julien@danjou.info>
-- @author Emmanuel Lepage Vallee <elv1313@gmail.com>
-- @copyright 2008 Julien Danjou
diff --git a/objects/client.c b/objects/client.c
index ce3e748d..2144c3c5 100644
--- a/objects/client.c
+++ b/objects/client.c
@@ -108,7 +108,28 @@
/** Client class.
*
- * @table object
+ * This table allow to add more dynamic properties to the clients. For example,
+ * doing:
+ *
+ * function awful.client.object.set_my_cool_property(c, value)
+ * -- Some logic code
+ * c._my_secret_my_cool_property = value
+ * c:emit_signal("property::my_cool_property)
+ * end
+ *
+ * function awful.client.object.get_my_cool_property()
+ * return c._my_secret_my_cool_property
+ * end
+ *
+ * Will add a new "my_cool_property" dyanmic property to all client. These
+ * methods will be called when an user does `c.my_cool_property = "something"`
+ * or set them in `awdul.rules`.
+ *
+ * Note that doing this isn't required to set random properties to the client,
+ * it is only useful when setting or getting these properties require code to
+ * executed.
+ *
+ * @table awful.object
*/
/** When a client gains focus.