Merge pull request #2982 from Elv13/modeline

Support shebangs (#!), modelines, API levels and add more doc.
This commit is contained in:
Emmanuel Lepage Vallée 2020-02-15 17:53:41 -05:00 committed by GitHub
commit 10e32198e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 924 additions and 117 deletions

View File

@ -69,6 +69,7 @@ set(AWE_SRCS
${BUILD_DIR}/strut.c
${BUILD_DIR}/systray.c
${BUILD_DIR}/xwindow.c
${BUILD_DIR}/options.c
${BUILD_DIR}/xkb.c
${BUILD_DIR}/xrdb.c
${BUILD_DIR}/common/atoms.c

View File

@ -3,6 +3,7 @@
#define AWESOME_VERSION "@AWESOME_VERSION@"
#define AWESOME_RELEASE "@AWESOME_RELEASE@"
#define AWESOME_API_LEVEL @AWESOME_API_LEVEL@
#endif //_AWE_VERSION_INTERNAL_H_

103
awesome.c
View File

@ -36,6 +36,7 @@
#include "spawn.h"
#include "systray.h"
#include "xwindow.h"
#include "options.h"
#include <getopt.h>
@ -552,26 +553,6 @@ true_config_callback(const char *unused)
return true;
}
/** Print help and exit(2) with given exit_code.
* \param exit_code The exit code.
*/
static void __attribute__ ((noreturn))
exit_help(int exit_code)
{
FILE *outfile = (exit_code == EXIT_SUCCESS) ? stdout : stderr;
fprintf(outfile,
"Usage: awesome [OPTION]\n\
-h, --help show help\n\
-v, --version show version\n\
-c, --config FILE configuration file to use\n\
--search DIR add a directory to the library search path\n\
-k, --check check configuration file syntax\n\
-a, --no-argb disable client transparency support\n\
-m, --screen on|off enable or disable automatic screen creation (default: on)\n\
-r, --replace replace an existing window manager\n");
exit(exit_code);
}
/** Hello, this is main.
* \param argc Who knows.
* \param argv Who knows.
@ -580,27 +561,15 @@ exit_help(int exit_code)
int
main(int argc, char **argv)
{
char *confpath = NULL;
string_array_t searchpath;
int xfd, opt;
int xfd;
xdgHandle xdg;
bool no_argb = false;
bool run_test = false;
bool replace_wm = false;
xcb_query_tree_cookie_t tree_c;
static struct option long_options[] =
{
{ "help", 0, NULL, 'h' },
{ "version", 0, NULL, 'v' },
{ "config", 1, NULL, 'c' },
{ "check", 0, NULL, 'k' },
{ "search", 1, NULL, 's' },
{ "no-argb", 0, NULL, 'a' },
{ "replace", 0, NULL, 'r' },
{ "screen" , 1, NULL, 'm' },
{ "reap", 1, NULL, '\1' },
{ NULL, 0, NULL, 0 }
};
/* The default values for the init flags */
int default_init_flags = INIT_FLAG_NONE
| INIT_FLAG_ARGB
| INIT_FLAG_AUTO_SCREEN;
/* Make stdout/stderr line buffered. */
setvbuf(stdout, NULL, _IOLBF, 0);
@ -611,6 +580,7 @@ main(int argc, char **argv)
globalconf.keygrabber = LUA_REFNIL;
globalconf.mousegrabber = LUA_REFNIL;
globalconf.exit_code = EXIT_SUCCESS;
globalconf.api_level = awesome_default_api_level();
buffer_init(&globalconf.startup_errors);
string_array_init(&searchpath);
@ -620,49 +590,11 @@ main(int argc, char **argv)
/* Text won't be printed correctly otherwise */
setlocale(LC_CTYPE, "");
/* check args */
while((opt = getopt_long(argc, argv, "vhkc:arm:",
long_options, NULL)) != -1)
switch(opt)
{
case 'v':
eprint_version();
break;
case 'h':
exit_help(EXIT_SUCCESS);
break;
case 'k':
run_test = true;
break;
case 'c':
if (confpath != NULL)
fatal("--config may only be specified once");
confpath = a_strdup(optarg);
break;
case 'm':
/* Validation */
if ((!optarg) || !(A_STREQ(optarg, "off") || A_STREQ(optarg, "on")))
fatal("The possible values of -m/--screen are \"on\" or \"off\"");
char *confpath = options_detect_shebang(argc, argv);
globalconf.no_auto_screen = A_STREQ(optarg, "off");
break;
case 's':
string_array_append(&searchpath, a_strdup(optarg));
break;
case 'a':
no_argb = true;
break;
case 'r':
replace_wm = true;
break;
case '\1':
/* Silently ignore --reap and its argument */
break;
default:
exit_help(EXIT_FAILURE);
break;
}
/* if no shebang is detected, check the args. Shebang (#!) args are parsed later */
if (!confpath)
confpath = options_check_args(argc, argv, &default_init_flags, &searchpath);
/* Get XDG basedir data */
if(!xdgInitHandle(&xdg))
@ -681,7 +613,8 @@ main(int argc, char **argv)
string_array_append(&searchpath, entry);
}
if (run_test)
/* Check the configfile syntax and exit */
if (default_init_flags & INIT_FLAG_RUN_TEST)
{
bool success = true;
/* Get the first config that will be tried */
@ -710,6 +643,10 @@ main(int argc, char **argv)
}
}
/* Parse `rc.lua` to see if it has an AwesomeWM modeline */
if (!(default_init_flags & INIT_FLAG_FORCE_CMD_ARGS))
options_init_config(awesome_argv[0], confpath, &default_init_flags, &searchpath);
/* Setup pipe for SIGCHLD processing */
{
if (!g_unix_open_pipe(sigchld_pipe, FD_CLOEXEC, NULL))
@ -751,7 +688,7 @@ main(int argc, char **argv)
globalconf.screen = xcb_aux_get_screen(globalconf.connection, globalconf.default_screen);
globalconf.default_visual = draw_default_visual(globalconf.screen);
if(!no_argb)
if(default_init_flags & INIT_FLAG_ARGB)
globalconf.visual = draw_argb_visual(globalconf.screen);
if(!globalconf.visual)
globalconf.visual = globalconf.default_visual;
@ -794,7 +731,7 @@ main(int argc, char **argv)
draw_test_cairo_xcb();
/* Acquire the WM_Sn selection */
acquire_WM_Sn(replace_wm);
acquire_WM_Sn(default_init_flags & INIT_FLAG_REPLACE_WM);
/* initialize dbus */
a_dbus_init();

View File

@ -271,6 +271,7 @@ else()
set(AWESOME_MAN_PATH ${CMAKE_INSTALL_PREFIX}/share/man CACHE PATH "awesome manpage directory")
endif()
# Hide to avoid confusion
mark_as_advanced(CMAKE_INSTALL_CMAKE_INSTALL_PREFIX)
@ -280,6 +281,7 @@ set(AWESOME_SYSCONFDIR ${XDG_CONFIG_DIR}/${PROJECT_AWE_NAME})
set(AWESOME_LUA_LIB_PATH ${AWESOME_DATA_PATH}/lib)
set(AWESOME_ICON_PATH ${AWESOME_DATA_PATH}/icons)
set(AWESOME_THEMES_PATH ${AWESOME_DATA_PATH}/themes)
set(AWESOME_API_LEVEL 4)
# }}}
if(GENERATE_DOC)

View File

@ -1,3 +1,4 @@
-- awesome_mode: api-level=4:screen=on
-- If LuaRocks is installed, make sure that packages installed through it are
-- found (e.g. lgi). If LuaRocks is not installed, do nothing.
pcall(require, "luarocks.loader")
@ -537,3 +538,8 @@ client.connect_signal("request::titlebars", function(c)
}
end)
-- }}}
-- Enable sloppy focus, so that focus follows mouse.
client.connect_signal("mouse::enter", function(c)
c:activate { context = "mouse_enter", raise = false }
end)

View File

@ -21,6 +21,7 @@
#include "config.h"
#include "common/version.h"
#include "globalconf.h"
#include "awesome-version-internal.h"
#include <stdlib.h>
@ -67,16 +68,28 @@ eprint_version(void)
printf("awesome %s (%s)\n"
" • Compiled against %s (running with %s)\n"
" • API level: %d\n"
" • D-Bus support: %s\n"
" • xcb-errors support: %s\n"
" • execinfo support: %s\n"
" • xcb-randr version: %d.%d\n"
" • LGI version: %s\n",
AWESOME_VERSION, AWESOME_RELEASE,
LUA_RELEASE, lua_tostring(L, -2),
has_dbus, has_xcb_errors, has_execinfo,
XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION,
lua_tostring(L, -1));
" • LGI version: %s\n"
" • Transparency enabled: %s\n"
" • Custom search paths: %s\n",
/* version */ AWESOME_VERSION,
/* release */ AWESOME_RELEASE,
/* Lua linked */ LUA_RELEASE,
/* Lua runtime */ lua_tostring(L, -2),
/* API Level */ globalconf.api_level,
/* DBus */ has_dbus,
/* XCB Error */ has_xcb_errors,
/* Execinfo */ has_execinfo,
/* XRandR major */ XCB_RANDR_MAJOR_VERSION,
/* XRandR minor */ XCB_RANDR_MINOR_VERSION,
/* LGI version */ lua_tostring(L, -1),
/* ARGB support */ globalconf.had_overriden_depth ? "no" : "yes",
/* Search path */ globalconf.have_searchpaths ? "yes" : "no"
);
lua_close(L);
exit(EXIT_SUCCESS);
@ -100,4 +113,11 @@ awesome_release_string(void)
return AWESOME_RELEASE;
}
int
awesome_default_api_level(void)
{
return AWESOME_API_LEVEL;
}
// vim: filetype=c:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80

View File

@ -25,6 +25,7 @@
void eprint_version(void) __attribute__ ((noreturn));
const char *awesome_version_string(void);
const char *awesome_release_string(void);
int awesome_default_api_level(void);
#endif

279
docs/09-options.md Normal file
View File

@ -0,0 +1,279 @@
# Startup options
This document explains how to control how AwesomeWM behaves before `rc.lua` is
executed.
## Command line
AwesomeWM has the following command line options:
Usage: awesome [OPTION]
-h, --help show help
-v, --version show version
-c, --config FILE configuration file to use
-f --force ignore modelines and apply the command line arguments
-s, --search DIR add a directory to the library search path
-k, --check check configuration file syntax
-a, --no-argb disable client transparency support
-l --api-level LEVEL select a different API support level than the current version
-m, --screen on|off enable or disable automatic screen creation (default: on)
-r, --replace replace an existing window manager
## Modelines
Usually, AwesomeWM is started using a session manager rather than directly using
the command line. On top of that, to make `rc.lua` more portable, it is possible
to set many of those options directly in your config file. They will be
interpreted before the Lua virtual machine is started. The keys are:
<table class='widget_list' border=1>
<tr style='font-weight: bold;'>
<th align='center'>Name</th>
<th align='center'>Has argument</th>
<th align='center'>Allow many</th>
<th align='center'>Type</th>
<th align='center'>Description</th>
</tr>
<tr><td>search</td><td>Yes</td><td>Yes</td><td>string</td><td>Path where the AwesomeWM core libraries are located</td></tr>
<tr><td>no-argb</td><td>No</td><td>No</td><td>N/A</td><td>Disable built-in (real) transparency.</td></tr>
<tr><td>api-level</td><td>Yes</td><td>No</td><td>integer</td><td>The config API level.</td></tr>
<tr><td>screen</td><td>Yes</td><td>No</td><td>string</td><td>Create the screen before executing `rc.lua` (`on` or `off`)</td></tr>
<tr><td>replace</td><td>No</td><td>No</td><td>N/A</td><td>Replace the current window manager.</td></tr>
</table>
A `modeline` must be near the top of `rc.lua` and start with `-- awesome_mode:`.
The options are separated by `:`. If an option has a value, it is separated by
`=`. The default modeline is:
-- awesome_mode: api-level=4:screen=on
To display more deprecation errors, you can increase the API level by 2:
-- awesome_mode: api-level=6:screen=on
## #! (shebang) support.
It is possible to make some `.lua` file executable and use the POSIX magic
prefix (`#!`, often referred as the shebang operator). AwesomeWM will attempt
to parse the header. Note that for now UTF-8 paths are not supported. A tipical
file header will look like:
#! /usr/bin/env awesome --replace
-- If LuaRocks is installed, make sure that packages installed through it
-- are found (e.g. lgi). If LuaRocks is not installed, do nothing.
pcall(require, "luarocks.loader")
-- Standard awesome library
local gears = require("gears")
[... more `rc.lua` content ...]
Then you can make the script executable with `chmod +x` and run it.
## Options description
### version (-v)
<table class='widget_list' border=1>
<tr style='font-weight: bold;'>
<th align='center'>Command line</th>
<th align='center'>Modeline</th>
<th align='center'>Shebang</th>
<tr>
<td align='center'>Yes</td>
<td align='center'>No</td>
<td align='center'>No</td></tr>
</tr>
</table>
The typical output will look like:
awesome v4.3 (Too long)
• Compiled against Lua 5.1.5 (running with Lua 5.1)
• API level: 4
• D-Bus support: yes
• xcb-errors support: no
• execinfo support: yes
• xcb-randr version: 1.6
• LGI version: 0.9.2
• Transparency enabled: yes
• Custom search paths: no
The content is useful when reporting a bug.
### config (-c): Alternate config path.
<table class='widget_list' border=1>
<tr style='font-weight: bold;'>
<th align='center'>Command line</th>
<th align='center'>Modeline</th>
<th align='center'>Shebang</th>
<tr>
<td align='center'>Yes</td>
<td align='center'>No</td>
<td align='center'>No</td></tr>
</tr>
</table>
This option allows to pass an arbitrary Lua file to be used as a config rather
than `~/.config/awesome/rc.lua` or `/etc/xdg/awesome/rc.lua`. It makes zero
sense in the shebang since you invoke a script directly. It doesn't make sense
in the modeline either since at that point the file is already being read.
### force (-f): Use the command line arguments even when the modeline disagree.
<table class='widget_list' border=1>
<tr style='font-weight: bold;'>
<th align='center'>Command line</th>
<th align='center'>Modeline</th>
<th align='center'>Shebang</th>
<tr>
<td align='center'>Yes</td>
<td align='center'>No</td>
<td align='center'>No</td></tr>
</tr>
</table>
Usually, modeslines have the final say of which options to enable. This allows
`rc.lua` to be portable between computers without have to modify `~/.xinitrc`
or your session files. However, sometime, it is useful to override these
parameters. The most common use case is the to bump the API level to see more
deprecation warnings.
### search (-s): Add directories to the Lua search paths.
<table class='widget_list' border=1>
<tr style='font-weight: bold;'>
<th align='center'>Command line</th>
<th align='center'>Modeline</th>
<th align='center'>Shebang</th>
<tr>
<td align='center'>Yes</td>
<td align='center'>Yes</td>
<td align='center'>Yes</td>
</tr>
</tr>
</table>
This option allows to add more paths to the Lua search path. It can be used
to set an alternate version of the core libraries such as `awful` to make
upstream patches development easier. It can also be used to point to custom
modules. It is usually recommended to place custom modules in
`~/.config/awesome/` or `/usr/share/awesome/lib`. Again, this option is mostly
useful for development.
### check (-k): Check the config **SYNTAX**.
<table class='widget_list' border=1>
<tr style='font-weight: bold;'>
<th align='center'>Command line</th>
<th align='center'>Modeline</th>
<th align='center'>Shebang</th>
<tr>
<td align='center'>Yes</td>
<td align='center'>No</td>
<td align='center'>No</td></tr>
</tr>
</table>
This option only check if the file is a valid Lua script. It will not check if
your custom logic makes sense. Even when this options says your config is fine,
it does **NOT** mean it can load properly. It only means it can be parsed and
the interpreter can attempt to execute it.
### no-argb (-a): Mitigate buggy graphics drivers.
<table class='widget_list' border=1>
<tr style='font-weight: bold;'>
<th align='center'>Command line</th>
<th align='center'>Modeline</th>
<th align='center'>Shebang</th>
<tr>
<td align='center'>Yes</td>
<td align='center'>Yes</td>
<td align='center'>Yes</td>
</tr>
</tr>
</table>
This options disable the built-in real transparency support. This means
titlebars and wiboxes can no longer be made fully transparent. If you don't
use a compositing manager such as `compton` or `picom`, this will only improve
reliability and portability. Transparency is enabled by default and this should
only be used when your config misbehave with a popular graphics driver. If it
does, please notify them. The bug is on their side.
### api-level (-l): Force your config to use a different API version.
<table class='widget_list' border=1>
<tr style='font-weight: bold;'>
<th align='center'>Command line</th>
<th align='center'>Modeline</th>
<th align='center'>Shebang</th>
<tr>
<td align='center'>Yes</td>
<td align='center'>Yes</td>
<td align='center'>Yes</td>
</tr>
</tr>
</table>
If you invested a lot of effort into a configuration and a new major version
is AwesomeWM is released, you might want to postpone having to update everything
until you are ready to begin using the new features. If the API level is set,
AwesomeWM will try to honor the behavior and content of the previous APIs.
You can also set this value forward to get notified faster of your usage of
newly deprecated API and enjoy cutting edge experimental features.
The default API level matches the first component of the AwesomeWM version.
For example, AwesomeWM v4.3 API level is "4". This only goes back to AwesomeWM
4.0. The older 3.x APIs have been removed.
### screen (-m): Set the screen creation behavior.
<table class='widget_list' border=1>
<tr style='font-weight: bold;'>
<th align='center'>Command line</th>
<th align='center'>Modeline</th>
<th align='center'>Shebang</th>
<tr>
<td align='center'>Yes</td>
<td align='center'>Yes</td>
<td align='center'>Yes</td>
</tr>
</tr>
</table>
This option changes *when* screen objects are created. In AwesomeWM 4.x, by
default, they are created before `rc.lua` is parsed. This is very convenient
for setup where the number of screen never changes. By creating them before
`rc.lua`, it is possible to make many assumptions such as `mouse.screen` and
`awful.screen.focused()` to always point to a valid screen. This is the `on`
behavior. The main downside is that when the number of screen changes or when
the DPI must be modified, all the automatic magic becomes very inconvenient.
When set to `off`, the screens are created early when executing `rc.lua`. This
has the advantages of sending multiple signals and giving a lot more
configuration options for features like the DPI or ultra-wide monitor. It also
make it easier to add and remove screens dynamically since they are fully. In
the future, the default will be `off` to allow HiDPI support to be enabled by
default.
controllable by Lua code.
### replace (-r): Replace the current window manager with AwesomeWM.
<table class='widget_list' border=1>
<tr style='font-weight: bold;'>
<th align='center'>Command line</th>
<th align='center'>Modeline</th>
<th align='center'>Shebang</th>
<tr>
<td align='center'>Yes</td>
<td align='center'>Yes</td>
<td align='center'>Yes</td>
</tr>
</tr>
</table>
If this option is set, AwesomeWM will kill the current window manager (even
if it is another `awesome` instance and replace it. This is disabled by default.

View File

@ -38,6 +38,7 @@ topics={
'05-awesomerc.md',
'06-appearance.md',
'07-my-first-awesome.md',
'09-options.md',
'16-using-cairo.md',
'17-porting-tips.md',
'90-FAQ.md',

View File

@ -126,6 +126,10 @@ typedef struct
bool have_xkb;
/** Check for XFixes extension */
bool have_xfixes;
/** Custom searchpaths are present, the runtime is tinted */
bool have_searchpaths;
/** When --no-argb is used in the modeline or command line */
bool had_overriden_depth;
uint8_t event_base_shape;
uint8_t event_base_xkb;
uint8_t event_base_randr;
@ -224,6 +228,8 @@ typedef struct
xcb_generic_event_t *pending_event;
/** The exit code that main() will return with */
int exit_code;
/** The Global API level */
int api_level;
} awesome_t;
extern awesome_t globalconf;

View File

@ -8,8 +8,11 @@
---------------------------------------------------------------------------
require("awful.permissions._common")._deprecated_autofocus_in_use()
--require("gears.debug").deprecate(
-- "The `awful.autofocus` module is deprecated, remove the require() and "..
-- "look at the new `rc.lua` granted permission section in the client rules",
-- {deprecated_in=5}
--)
if awesome.api_level > 4 then
--TODO v5: Remove `require("awful.autofocus")` from `rc.lua`.
require("gears.debug").deprecate(
"The `awful.autofocus` module is deprecated, remove the require() and "..
"look at the new `rc.lua` granted permission section in the client rules",
{deprecated_in=5}
)
end

View File

@ -740,9 +740,14 @@ screen.connect_signal("property::workarea", function(s)
end)
-- Enable sloppy focus, so that focus follows mouse.
client.connect_signal("mouse::enter", function(c)
c:emit_signal("request::autoactivate", "mouse_enter", {raise=false})
end)
if awesome.api_level > 4 then
--TODO v5: Remove the code from `rc.lua`. It cannot be done yet because we
-- cannot know if the user removed it to disable sloppy focus or because
-- they want to use the permissions to manage it.
client.connect_signal("mouse::enter", function(c)
c:emit_signal("request::autoactivate", "mouse_enter", {raise=false})
end)
end
return permissions

View File

@ -82,7 +82,8 @@ local displayed_deprecations = {}
--- Display a deprecation notice, but only once per traceback.
--
-- This function also emits the `debug::deprecation` signal on the `awesome`
-- global object.
-- global object. If the deprecated API has been deprecated for more than one
-- API level, it will also send a non-fatal error.
--
-- @param[opt] see The message to a new method / function to use.
-- @tparam table args Extra arguments
@ -90,17 +91,18 @@ local displayed_deprecations = {}
-- @tparam integer args.deprecated_in Print the message only when Awesome's
-- version is equal to or greater than deprecated_in.
-- @staticfct gears.debug.deprecate
-- @emits debug::deprecation
-- @emitstparam @emitstparam string msg The full formatted message.
-- @emitstparam @emitstparam string see A message provided by the caller.
-- @emitstparam @emitstparam table args Some extra context.
-- @emits debug::deprecation This is usually routed to stdout when the API is
-- newly deprecated.
-- @emitstparam debug::deprecation string msg The full formatted message.
-- @emitstparam debug::deprecation string see A message provided by the caller.
-- @emitstparam debug::deprecation table args Some extra context.
-- @emits debug::error When the API has been deprecated for more than
-- one API level.
-- @emitstparam debug::error string msg The full formatted message.
function debug.deprecate(see, args)
args = args or {}
if args.deprecated_in then
local dep_ver = "v" .. tostring(args.deprecated_in)
if awesome.version < dep_ver then
return
end
if args.deprecated_in and awesome.api_level < args.deprecated_in then
return
end
local tb = _G.debug.traceback()
if displayed_deprecations[tb] then
@ -123,9 +125,15 @@ function debug.deprecate(see, args)
end
debug.print_warning(msg .. ".\n" .. tb)
if awesome then
if awesome and awesome.api_level == args.deprecated_in then
awesome.emit_signal("debug::deprecation", msg, see, args)
end
if args.deprecated_in and awesome.api_level > args.deprecated_in then
awesome.emit_signal(
"debug::error", msg, false
)
end
end
--- Create a class proxy with deprecation messages.
@ -140,11 +148,8 @@ end
-- @staticfct gears.debug.deprecate_class
function debug.deprecate_class(fallback, old_name, new_name, args)
args = args or {}
if args.deprecated_in then
local dep_ver = "v" .. tostring(args.deprecated_in)
if awesome.version < dep_ver then
return fallback
end
if args.deprecated_in and awesome.api_level < args.deprecated_in then
return fallback
end
local message = old_name.." has been renamed to "..new_name

37
luaa.c
View File

@ -540,15 +540,42 @@ static int luaA_get_active_modifiers(lua_State *L)
}
/**
* The version of awesome.
* The AwesomeWM version.
* @tfield string version
*/
/**
* The release name of awesome.
* The AwesomeWM release name.
* @tfield string release
*/
/**
* The AwesomeWM API level.
*
* By default, this matches the major version (first component of the version).
*
* API levels are used to allow newer version of AwesomeWM to alter the behavior
* and subset deprecated APIs. Using an older API level than the current major
* version allows to use legacy `rc.lua` with little porting. However, they wont
* be able to use all the new features. Attempting to use a newer feature along
* with an older API level is not and will not be supported, even if it almost
* works. Keeping up to date with the newer API levels is highly recommended.
*
* Going the other direction, setting an higher API level allows to take
* advantage of experimental feature. It will also be much harsher when it comes
* to deprecation. Setting the API level value beyond `current+3` will treat
* using APIs currently pending deprecation as fatal errors. All new code
* submitted to the upstream AwesomeWM codebase is forbidden to use deprecated
* APIs. Testing your patches with mode and the default config is recommended
* before submitting a patch.
*
* You can use the `-l` command line option or `api-level` modeline key to set
* the API level for your `rc.lua`. This setting is global and read only,
* individual modules cannot set their own API level.
*
* @tfield string api_level
*/
/**
* The configuration file which has been loaded.
* @tfield string conffile
@ -616,6 +643,12 @@ luaA_awesome_index(lua_State *L)
return 1;
}
if(A_STREQ(buf, "api_level"))
{
lua_pushinteger(L, globalconf.api_level);
return 1;
}
if(A_STREQ(buf, "startup"))
{
lua_pushboolean(L, globalconf.loop == NULL);

466
options.c Normal file
View File

@ -0,0 +1,466 @@
/*
* stack.h - client stack management header
*
* Copyright © 2020 Emmanuel Lepage-Vallee <elv1313@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include "options.h"
#include "common/version.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <getopt.h>
#define KEY_VALUE_BUF_MAX 64
#define READ_BUF_MAX 127
static void
set_api_level(char *value)
{
if (!value)
return;
char *ptr;
int ret = strtol(value, &ptr, 10);
/* There is no valid number at all */
if (value == ptr) {
fprintf(stderr, "Invalid API level %s\n", value);
return;
}
/* There is a number, but also letters, this is invalid */
if (ptr[0] != '\0' && ptr[0] != '.') {
fprintf(stderr, "Invalid API level %s\n", value);
return;
}
/* This API level doesn't exist, fallback to v4 */
if (ret < 4)
ret = 4;
globalconf.api_level = ret;
}
static void
push_arg(string_array_t *args, char *value, size_t *len)
{
value[*len] = '\0';
string_array_append(args, a_strdup(value));
(*len) = 0;
}
/*
* Support both shebang and modeline modes.
*/
bool
options_init_config(char *execpath, char *configpath, int *init_flags, string_array_t *paths)
{
/* The different state the parser can have. */
enum {
MODELINE_STATE_INIT , /* Start of line */
MODELINE_STATE_NEWLINE , /* Start of line */
MODELINE_STATE_COMMENT , /* until -- */
MODELINE_STATE_MODELINE , /* until awesome_mode: */
MODELINE_STATE_SHEBANG , /* until ! */
MODELINE_STATE_KEY_DELIM , /* until the key begins */
MODELINE_STATE_KEY , /* until '=' */
MODELINE_STATE_VALUE_DELIM, /* after ':' */
MODELINE_STATE_VALUE , /* until ',' or '\n' */
MODELINE_STATE_COMPLETE , /* Parsing is done */
MODELINE_STATE_INVALID , /* note a modeline */
MODELINE_STATE_ERROR /* broken modeline */
} state = MODELINE_STATE_INIT;
/* The parsing mode */
enum {
MODELINE_MODE_NONE , /* No modeline */
MODELINE_MODE_LINE , /* modeline */
MODELINE_MODE_SHEBANG, /* #! shebang */
} mode = MODELINE_MODE_NONE;
static const unsigned char name[] = "awesome_mode:";
static char key_buf [KEY_VALUE_BUF_MAX+1] = {'\0'};
static char file_buf[READ_BUF_MAX+1 ] = {'\0'};
size_t pos = 0;
string_array_t argv;
string_array_init(&argv);
string_array_append(&argv, a_strdup(execpath));
FILE *fp = fopen(configpath, "r");
/* Share the error codepath with parsing errors */
if (!fp)
return false;
/* Try to read the first line */
if (!fgets(file_buf, READ_BUF_MAX, fp)) {
fclose(fp);
return false;
}
unsigned char c;
/* Simple state machine to translate both modeline and shebang into argv */
for (int i = 0; (c = file_buf[i++]) != '\0';) {
/* Be very permissive, skip the unknown, UTF is not allowed */
if ((c > 126 || c < 32) && c != 10 && c != 13 && c != 9) {
static bool once = true;
/* Print a warning once */
if (once) {
fprintf(stderr, "WARNING: modelines must use ASCII\n");
once = false;
}
continue;
}
switch (state) {
case MODELINE_STATE_INIT:
switch (c) {
case '#':
state = MODELINE_STATE_SHEBANG;
break;
case ' ': case '-':
state = MODELINE_STATE_COMMENT;
break;
default:
state = MODELINE_STATE_INVALID;
}
break;
case MODELINE_STATE_NEWLINE:
switch (c) {
case ' ': case '-':
state = MODELINE_STATE_COMMENT;
break;
default:
state = MODELINE_STATE_INVALID;
}
break;
case MODELINE_STATE_COMMENT:
switch (c) {
case '-':
state = MODELINE_STATE_MODELINE;
break;
default:
state = MODELINE_STATE_INVALID;
}
break;
case MODELINE_STATE_MODELINE:
if (c == ' ')
break;
else if (c != name[pos++]) {
state = MODELINE_STATE_INVALID;
pos = 0;
}
if (pos == 13) {
pos = 0;
state = MODELINE_STATE_KEY_DELIM;
mode = MODELINE_MODE_LINE;
}
break;
case MODELINE_STATE_SHEBANG:
switch(c) {
case '!':
mode = MODELINE_MODE_SHEBANG;
state = MODELINE_STATE_KEY_DELIM;
break;
default:
state = MODELINE_STATE_INVALID;
}
break;
case MODELINE_STATE_KEY_DELIM:
switch (c) {
case ' ': case '\t': case ':': case '=':
break;
case '\n': case '\r':
state = MODELINE_STATE_ERROR;
break;
default:
/* In modeline mode, assume all keys are the long name */
switch(mode) {
case MODELINE_MODE_LINE:
strcpy(key_buf, "--");
pos = 2;
break;
case MODELINE_MODE_SHEBANG:
case MODELINE_MODE_NONE:
break;
};
state = MODELINE_STATE_KEY;
key_buf[pos++] = c;
}
break;
case MODELINE_STATE_KEY:
switch (c) {
case '=':
push_arg(&argv, key_buf, &pos);
state = MODELINE_STATE_VALUE_DELIM;
break;
case ' ': case '\t': case ':':
push_arg(&argv, key_buf, &pos);
state = MODELINE_STATE_KEY_DELIM;
break;
default:
key_buf[pos++] = c;
}
break;
case MODELINE_STATE_VALUE_DELIM:
switch (c) {
case ' ': case '\t':
break;
case '\n': case '\r':
state = MODELINE_STATE_ERROR;
break;
case ':':
state = MODELINE_STATE_KEY_DELIM;
break;
default:
state = MODELINE_STATE_VALUE;
key_buf[pos++] = c;
}
break;
case MODELINE_STATE_VALUE:
switch(c) {
case ',': case ' ': case ':': case '\t':
push_arg(&argv, key_buf, &pos);
state = MODELINE_STATE_KEY_DELIM;
break;
case '\n': case '\r':
state = MODELINE_STATE_COMPLETE;
break;
default:
key_buf[pos++] = c;
}
break;
case MODELINE_STATE_INVALID:
/* This cannot happen, the `if` below should prevent it */
state = MODELINE_STATE_ERROR;
break;
case MODELINE_STATE_COMPLETE:
case MODELINE_STATE_ERROR:
break;
}
/* No keys or values are that large */
if (pos >= KEY_VALUE_BUF_MAX)
state = MODELINE_STATE_ERROR;
/* Stop parsing when completed */
if (state == MODELINE_STATE_ERROR || state == MODELINE_STATE_COMPLETE)
break;
/* Try the next line */
if (((i == READ_BUF_MAX || file_buf[i+1] == '\0') && !feof(fp)) || state == MODELINE_STATE_INVALID) {
if (state == MODELINE_STATE_KEY || state == MODELINE_STATE_VALUE)
push_arg(&argv, key_buf, &pos);
/* Skip empty lines */
do {
if (fgets(file_buf, READ_BUF_MAX, fp))
state = MODELINE_STATE_NEWLINE;
else {
state = argv.len ? MODELINE_STATE_COMPLETE : MODELINE_STATE_ERROR;
break;
}
i = 0; /* Always reset `i` to avoid an unlikely invalid read */
} while (i == 0 && file_buf[0] == '\n');
}
}
fclose(fp);
/* Reset the global POSIX args counter */
optind = 0;
/* Be future proof, allow let unknown keys live, let the Lua code decide */
(*init_flags) |= INIT_FLAG_ALLOW_FALLBACK;
options_check_args(argv.len, argv.tab, init_flags, paths);
/* Cleanup */
string_array_wipe(&argv);
return state == MODELINE_STATE_COMPLETE;
}
char *
options_detect_shebang(int argc, char **argv)
{
/* There is no cross-platform ways to check if it is *really* called by a
* shebang. There is a couple Linux specific hacks which work with the
* most common C libraries, but they wont work on *BSD.
*
* On some platforms, the argv is going to be parsed by the OS, in other
* they will be concatenated in one big string. There is some ambiguities
* caused by that. For example, `awesome -s foo` and and `#!/bin/awesome -s`
* are both technically valid if `foo` is a directory in the first and
* lua file (without extension) in the second. While `-s` with a file
* wont work, it is hard to know by looking at the string.
*
* The trick to avoid any ambiguity is to just read the file and see if
* the args match. `options_init_config` will be called later and the args
* will be parsed properly.
*/
/* On WSL and some other *nix this isn't true, but it is true often enough */
if (argc > 3 || argc == 1)
return NULL;
/* Check if it is executable */
struct stat inf;
if (stat(argv[argc-1], &inf) || !(inf.st_mode & S_IXUSR))
return NULL;
FILE *fp = fopen(argv[argc-1], "r");
if (!fp)
return NULL;
char buf[3];
if (!fgets(buf, 2, fp)) {
fclose(fp);
return NULL;
}
fclose(fp);
if (!strcmp(buf, "#!"))
return NULL;
/* Ok, good enough, this is a shebang script, assume it called `awesome` */
return a_strdup(argv[argc-1]);
}
/** Print help and exit(2) with given exit_code.
* \param exit_code The exit code.
*/
static void __attribute__ ((noreturn))
exit_help(int exit_code)
{
FILE *outfile = (exit_code == EXIT_SUCCESS) ? stdout : stderr;
fprintf(outfile,
"Usage: awesome [OPTION]\n\
-h, --help show help\n\
-v, --version show version\n\
-c, --config FILE configuration file to use\n\
-f, --force ignore modelines and apply the command line arguments\n\
-s, --search DIR add a directory to the library search path\n\
-k, --check check configuration file syntax\n\
-a, --no-argb disable client transparency support\n\
-l --api-level LEVEL select a different API support level than the current version \n\
-m, --screen on|off enable or disable automatic screen creation (default: on)\n\
-r, --replace replace an existing window manager\n");
exit(exit_code);
}
#define ARG 1
#define NO_ARG 0
char *
options_check_args(int argc, char **argv, int *init_flags, string_array_t *paths)
{
static struct option long_options[] =
{
{ "help" , NO_ARG, NULL, 'h' },
{ "version" , NO_ARG, NULL, 'v' },
{ "config" , ARG , NULL, 'c' },
{ "force" , NO_ARG, NULL, 'f' },
{ "check" , NO_ARG, NULL, 'k' },
{ "search" , ARG , NULL, 's' },
{ "no-argb" , NO_ARG, NULL, 'a' },
{ "replace" , NO_ARG, NULL, 'r' },
{ "screen" , ARG , NULL, 'm' },
{ "api-level" , ARG , NULL, 'l' },
{ "reap" , ARG , NULL, '\1' },
{ NULL , NO_ARG, NULL, 0 }
};
char *confpath = NULL;
int opt;
while((opt = getopt_long(argc, argv, "vhkc:arml:",
long_options, NULL)) != -1) {
switch(opt)
{
case 'v':
eprint_version();
break;
case 'h':
if (! ((*init_flags) & INIT_FLAG_ALLOW_FALLBACK))
exit_help(EXIT_SUCCESS);
break;
case 'f':
(*init_flags) |= INIT_FLAG_FORCE_CMD_ARGS;
break;
case 'k':
(*init_flags) |= INIT_FLAG_RUN_TEST;
break;
case 'c':
if (confpath != NULL)
fatal("--config may only be specified once");
confpath = a_strdup(optarg);
break;
case 'm':
/* Validation */
if ((!optarg) || !(A_STREQ(optarg, "off") || A_STREQ(optarg, "on")))
fatal("The possible values of -m/--screen are \"on\" or \"off\"");
globalconf.no_auto_screen = A_STREQ(optarg, "off");
(*init_flags) &= ~INIT_FLAG_AUTO_SCREEN;
break;
case 's':
globalconf.have_searchpaths = true;
string_array_append(paths, a_strdup(optarg));
break;
case 'a':
globalconf.had_overriden_depth = true;
(*init_flags) &= ~INIT_FLAG_ARGB;
break;
case 'r':
(*init_flags) |= INIT_FLAG_REPLACE_WM;
break;
case 'l':
set_api_level(optarg);
break;
case '\1':
/* Silently ignore --reap and its argument */
break;
default:
if (! ((*init_flags) & INIT_FLAG_ALLOW_FALLBACK))
exit_help(EXIT_FAILURE);
break;
}}
return confpath;
}
#undef AR
#undef NO_ARG
#undef KEY_VALUE_BUF_MAX
#undef READ_BUF_MAX

39
options.h Normal file
View File

@ -0,0 +1,39 @@
/*
* stack.h - client stack management header
*
* Copyright © 2020 Emmanuel Lepage-Vallee <elv1313@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include "common/array.h"
#include "globalconf.h"
/**
* Initialization values extracted from the command line or modeline.
*/
typedef enum {
INIT_FLAG_NONE = 0x0,
INIT_FLAG_RUN_TEST = 0x1,
INIT_FLAG_ARGB = 0x1 << 1,
INIT_FLAG_REPLACE_WM = 0x1 << 2,
INIT_FLAG_AUTO_SCREEN = 0x1 << 3,
INIT_FLAG_ALLOW_FALLBACK = 0x1 << 4,
INIT_FLAG_FORCE_CMD_ARGS = 0x1 << 5,
} awesome_init_config_t;
char *options_detect_shebang(int argc, char **argv);
bool options_init_config(char *execpath, char *configpath, int *init_flags, string_array_t *paths);
char *options_check_args(int argc, char **argv, int *init_flags, string_array_t *paths);

View File

@ -15,6 +15,7 @@ describe("awful.permissions.client_geometry_requests", function()
connect_signal = function() end,
}
_G.awesome = {
api_level = 4,
connect_signal = function() end,
}
_G.drawin = {

View File

@ -83,7 +83,8 @@ function awesome.xrdb_get_value()
end
-- Always show deprecated messages
awesome.version = "v9999"
awesome.version = "v9999"
awesome.api_level = 9999
-- SVG are composited. Without it we need a root surface
awesome.composite_manager_running = true

View File

@ -115,7 +115,7 @@ fi
# shellcheck disable=SC2206
awesome_options=($AWESOME_OPTIONS $manual_screens --search lib --search "$this_dir")
awesome_options+=(--screen off)
awesome_options+=(--screen off --force)
# Cleanup on errors / aborting.
cleanup() {