From af4b4a254f268b57a0fe2e5e248c51e145c5a08c Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sat, 1 Feb 2020 21:27:55 -0500 Subject: [PATCH 01/12] init: Move the command option parsing to its own file. The next commit will add modeline support and share the code. --- CMakeLists.txt | 1 + awesome.c | 95 +++++-------------------------------- options.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++++ options.h | 36 ++++++++++++++ 4 files changed, 175 insertions(+), 83 deletions(-) create mode 100644 options.c create mode 100644 options.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 68d2accc3..5cc09518f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/awesome.c b/awesome.c index 86a4e7773..3cc3bef16 100644 --- a/awesome.c +++ b/awesome.c @@ -36,6 +36,7 @@ #include "spawn.h" #include "systray.h" #include "xwindow.h" +#include "options.h" #include @@ -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); @@ -621,48 +590,7 @@ main(int argc, char **argv) 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\""); - - 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; - } + char *confpath = options_check_args(argc, argv, &default_init_flags, &searchpath); /* Get XDG basedir data */ if(!xdgInitHandle(&xdg)) @@ -681,7 +609,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 */ @@ -751,7 +680,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 +723,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(); diff --git a/options.c b/options.c new file mode 100644 index 000000000..a66a6c416 --- /dev/null +++ b/options.c @@ -0,0 +1,126 @@ +/* + * stack.h - client stack management header + * + * Copyright © 2020 Emmanuel Lepage-Vallee + * + * 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 +#include +#include + +/** 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); +} + +#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' }, + { "check", NO_ARG, NULL, 'k' }, + { "search", ARG , NULL, 's' }, + { "no-argb", NO_ARG, NULL, 'a' }, + { "replace", NO_ARG, NULL, 'r' }, + { "screen" , ARG , NULL, 'm' }, + { "reap", ARG , NULL, '\1' }, + { NULL, NO_ARG, NULL, 0 } + }; + + char *confpath = NULL; + int opt; + + while((opt = getopt_long(argc, argv, "vhkc:arm:", + 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 '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': + string_array_append(paths, a_strdup(optarg)); + break; + case 'a': + (*init_flags) &= ~INIT_FLAG_ARGB; + break; + case 'r': + (*init_flags) |= INIT_FLAG_REPLACE_WM; + 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 diff --git a/options.h b/options.h new file mode 100644 index 000000000..cc0dfb337 --- /dev/null +++ b/options.h @@ -0,0 +1,36 @@ +/* + * stack.h - client stack management header + * + * Copyright © 2020 Emmanuel Lepage-Vallee + * + * 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, +} awesome_init_config_t; + +char *options_check_args(int argc, char **argv, int *init_flags, string_array_t *paths); From c6164002398bc9c37f1a2f2a518cf17504944783 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sat, 8 Feb 2020 17:11:26 -0500 Subject: [PATCH 02/12] cmd: Add an option to force the command line arguments. The next commit will add modeline support. By default, modelines have the final work on which options to set. However, mostly for testing, this isn't flexible enough. --- options.c | 5 +++++ options.h | 1 + tests/run.sh | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/options.c b/options.c index a66a6c416..2272da5dd 100644 --- a/options.c +++ b/options.c @@ -37,6 +37,7 @@ exit_help(int exit_code) "Usage: awesome [OPTION]\n\ -h, --help show help\n\ -v, --version show version\n\ + -f, --force ignore modelines and apply the command line arguments\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\ @@ -58,6 +59,7 @@ options_check_args(int argc, char **argv, int *init_flags, string_array_t *paths { "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' }, @@ -81,6 +83,9 @@ options_check_args(int argc, char **argv, int *init_flags, string_array_t *paths 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; diff --git a/options.h b/options.h index cc0dfb337..7a0d76aec 100644 --- a/options.h +++ b/options.h @@ -31,6 +31,7 @@ typedef enum { 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_check_args(int argc, char **argv, int *init_flags, string_array_t *paths); diff --git a/tests/run.sh b/tests/run.sh index 3a7046564..ec4395c6a 100755 --- a/tests/run.sh +++ b/tests/run.sh @@ -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() { From f1aedb237ad8c1f7b3df7b704aae2811608bb02e Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sat, 1 Feb 2020 21:29:26 -0500 Subject: [PATCH 03/12] init: Add modeline support. This commits adds the ability to add an `-- awesome_mode:` comment to `rc.lua`. This line will be interpreted before Lua starts and allow command line options to be set in the file. There is also a partial shebang mode (`#!`) support. While it is not yet possible to make a random Lua file executable and start `awesome`, it is at least supported to configure AwesomeWM. The next commit will add the missing bits. This commit implements the parsing using a state machine. While glib has its own functions, they don't do 100% of what we need. It could have been possible to use them anyway and get quotes, escaping and UTF-8 support for free. The downside would have been duplicated code to handle shebangs and modeline. The state machine code fully support 3 different ways of loading the arguments with the same code path. --- awesome.c | 4 + options.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ options.h | 1 + 3 files changed, 246 insertions(+) diff --git a/awesome.c b/awesome.c index 3cc3bef16..ffdeb92f3 100644 --- a/awesome.c +++ b/awesome.c @@ -639,6 +639,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)) diff --git a/options.c b/options.c index 2272da5dd..3a8378043 100644 --- a/options.c +++ b/options.c @@ -26,6 +26,247 @@ #include #include +#define KEY_VALUE_BUF_MAX 64 +#define READ_BUF_MAX 127 + +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) + 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; +} + /** Print help and exit(2) with given exit_code. * \param exit_code The exit code. */ diff --git a/options.h b/options.h index 7a0d76aec..a71fca8d7 100644 --- a/options.h +++ b/options.h @@ -34,4 +34,5 @@ typedef enum { INIT_FLAG_FORCE_CMD_ARGS = 0x1 << 5, } awesome_init_config_t; +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); From d2f89995950260decef32ef904ecce9867103d3a Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 2 Feb 2020 05:15:36 -0500 Subject: [PATCH 04/12] init: Support loading awesome with shebang (#!) executables. This commits re-use the modeline code from the previous commit to support Lua scripts starting with #!. Previously, it was non-trivial to add support since most *nix OS wont parse the command line arguments correctly. Since we now have a state machine good enough for 95%+ of the use case, it is easy to support them. --- awesome.c | 7 +++++-- options.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ options.h | 1 + 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/awesome.c b/awesome.c index ffdeb92f3..ba9c81517 100644 --- a/awesome.c +++ b/awesome.c @@ -589,8 +589,11 @@ main(int argc, char **argv) /* Text won't be printed correctly otherwise */ setlocale(LC_CTYPE, ""); - /* check args */ - char *confpath = options_check_args(argc, argv, &default_init_flags, &searchpath); + char *confpath = options_detect_shebang(argc, argv); + + /* 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)) diff --git a/options.c b/options.c index 3a8378043..380c7b523 100644 --- a/options.c +++ b/options.c @@ -24,6 +24,7 @@ #include #include +#include #include #define KEY_VALUE_BUF_MAX 64 @@ -267,6 +268,56 @@ options_init_config(char *execpath, char *configpath, int *init_flags, string_ar 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. */ diff --git a/options.h b/options.h index a71fca8d7..422507541 100644 --- a/options.h +++ b/options.h @@ -34,5 +34,6 @@ typedef enum { 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); From ee80fe052f65b7c8281d4a8b3fe50225e091f111 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 2 Feb 2020 18:57:26 -0500 Subject: [PATCH 05/12] init: Add an API level concept to `capi`. The API level concept is something used by other projects such as Android and iOS to allow deprecated features to be removed and the bahavior to be altered without breaking compability with existing code. The same will apply to AwesomeWM. The current API level is "4" and as long as config use this, no deprecation or bahavior change will be exposed. If the user sets it to an higher value or we release the next major version and new users start to use the, then current, default config, they will use the new API level. The the far future, if ever, we could fork the entire Lua libraries to support legacy APIs. This would only require to keep the core API support for those legacy calls. In the meantime, `gears.debug` will use this to manage the deprecation and some conditional code will be added as a last resort attempt to preserve behavior compatibility while moving forward with breaking changes. --- awesome-version-internal.h | 1 + awesome.c | 1 + awesomeConfig.cmake | 2 + common/version.c | 7 +++ common/version.h | 1 + globalconf.h | 2 + luaa.c | 37 +++++++++++++++- options.c | 76 +++++++++++++++++++++++--------- tests/examples/shims/awesome.lua | 3 +- 9 files changed, 106 insertions(+), 24 deletions(-) diff --git a/awesome-version-internal.h b/awesome-version-internal.h index 173c2d3cf..395d143d7 100644 --- a/awesome-version-internal.h +++ b/awesome-version-internal.h @@ -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_ diff --git a/awesome.c b/awesome.c index ba9c81517..c71699d91 100644 --- a/awesome.c +++ b/awesome.c @@ -580,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); diff --git a/awesomeConfig.cmake b/awesomeConfig.cmake index 4592001c0..3cf42b11b 100644 --- a/awesomeConfig.cmake +++ b/awesomeConfig.cmake @@ -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) diff --git a/common/version.c b/common/version.c index aae63fdb8..c32fa3cd1 100644 --- a/common/version.c +++ b/common/version.c @@ -100,4 +100,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 diff --git a/common/version.h b/common/version.h index 7bf3bc908..776df5580 100644 --- a/common/version.h +++ b/common/version.h @@ -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 diff --git a/globalconf.h b/globalconf.h index cf55493b1..990700d05 100644 --- a/globalconf.h +++ b/globalconf.h @@ -224,6 +224,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; diff --git a/luaa.c b/luaa.c index 3e9455f74..a5cb333ad 100644 --- a/luaa.c +++ b/luaa.c @@ -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); diff --git a/options.c b/options.c index 380c7b523..dc9b760da 100644 --- a/options.c +++ b/options.c @@ -24,12 +24,41 @@ #include #include +#include #include #include #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) { @@ -327,15 +356,16 @@ 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\ - -f, --force ignore modelines and apply the command line arguments\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"); + -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\ + --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); } @@ -348,23 +378,24 @@ 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' }, - { "reap", ARG , NULL, '\1' }, - { NULL, NO_ARG, NULL, 0 } + { "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:arm:", + while((opt = getopt_long(argc, argv, "vhkc:arml:", long_options, NULL)) != -1) { switch(opt) { @@ -405,6 +436,9 @@ options_check_args(int argc, char **argv, int *init_flags, string_array_t *paths 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; diff --git a/tests/examples/shims/awesome.lua b/tests/examples/shims/awesome.lua index ac4035725..64b0da43b 100644 --- a/tests/examples/shims/awesome.lua +++ b/tests/examples/shims/awesome.lua @@ -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 From e0b982fa1a64e6318f829b978dcc8ddca7a719a2 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 2 Feb 2020 19:35:57 -0500 Subject: [PATCH 06/12] gears.debug: Use the API level as the source of truth. Rather than the version. --- lib/gears/debug.lua | 37 +++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/lib/gears/debug.lua b/lib/gears/debug.lua index 54bbeba3f..49aac58e5 100644 --- a/lib/gears/debug.lua +++ b/lib/gears/debug.lua @@ -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 From d58b4c3d96046bd727f7c370bc3d6f296a3c2b53 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 2 Feb 2020 20:36:20 -0500 Subject: [PATCH 07/12] cmd: Add more info to --version. Now that modeline are supported, they will affect the content of version. This commits only adds the new information. `--version` has to be the last command line argument and the modeline isn't parsed yet. Another commit will make sure --version parses the modeline and finishes to process the command line arguments in case it isn't last. Please note that luarocks also affects data points such as the LGI version. It should be smarter and check if `rc.lua` has `require("luarocks")`. This commit does *not* do it. --- common/version.c | 25 +++++++++++++++++++------ globalconf.h | 4 ++++ options.c | 2 ++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/common/version.c b/common/version.c index c32fa3cd1..0fc481aa7 100644 --- a/common/version.c +++ b/common/version.c @@ -21,6 +21,7 @@ #include "config.h" #include "common/version.h" +#include "globalconf.h" #include "awesome-version-internal.h" #include @@ -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); diff --git a/globalconf.h b/globalconf.h index 990700d05..970849bf5 100644 --- a/globalconf.h +++ b/globalconf.h @@ -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; diff --git a/options.c b/options.c index dc9b760da..ed2a905b9 100644 --- a/options.c +++ b/options.c @@ -428,9 +428,11 @@ options_check_args(int argc, char **argv, int *init_flags, string_array_t *paths 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': From d755a877ff8a2e3d324855769e701d326d244a54 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 2 Feb 2020 20:56:27 -0500 Subject: [PATCH 08/12] doc: Add a new manual page describing the command line options. --- docs/09-options.md | 279 +++++++++++++++++++++++++++++++++++++++++++++ docs/config.ld | 1 + 2 files changed, 280 insertions(+) create mode 100644 docs/09-options.md diff --git a/docs/09-options.md b/docs/09-options.md new file mode 100644 index 000000000..da8eb472b --- /dev/null +++ b/docs/09-options.md @@ -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: + + + + + + + + + + + + + + +
NameHas argumentAllow manyTypeDescription
searchYesYesstringPath where the AwesomeWM core libraries are located
no-argbNoNoN/ADisable built-in (real) transparency.
api-levelYesNointegerThe config API level.
screenYesNostringCreate the screen before executing `rc.lua` (`on` or `off`)
replaceNoNoN/AReplace the current window manager.
+ +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) + + + + + + + + + + + +
Command lineModelineShebang
YesNoNo
+ +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. + + + + + + + + + + + +
Command lineModelineShebang
YesNoNo
+ +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. + + + + + + + + + + + +
Command lineModelineShebang
YesNoNo
+ +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. + + + + + + + + + + + + +
Command lineModelineShebang
YesYesYes
+ +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**. + + + + + + + + + + + +
Command lineModelineShebang
YesNoNo
+ +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. + + + + + + + + + + + + +
Command lineModelineShebang
YesYesYes
+ +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. + + + + + + + + + + + + +
Command lineModelineShebang
YesYesYes
+ +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. + + + + + + + + + + + + +
Command lineModelineShebang
YesYesYes
+ +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. + + + + + + + + + + + + +
Command lineModelineShebang
YesYesYes
+ +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. diff --git a/docs/config.ld b/docs/config.ld index f06d603a7..62699984a 100644 --- a/docs/config.ld +++ b/docs/config.ld @@ -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', From 3a6af7bf6c51249f270f9cdec08b1b8558861cc6 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 2 Feb 2020 20:57:47 -0500 Subject: [PATCH 09/12] doc: Document that `-s` is the alias for `--search`. For some reason it wasn't. --- options.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/options.c b/options.c index ed2a905b9..bfc3af56a 100644 --- a/options.c +++ b/options.c @@ -360,7 +360,7 @@ exit_help(int exit_code) -v, --version show version\n\ -c, --config FILE configuration file to use\n\ -f, --force ignore modelines and apply the command line arguments\n\ - --search DIR add a directory to the library search path\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\ From 11f0a466b007cf6840b83f3083f3e33ea1cf291a Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 2 Feb 2020 20:59:04 -0500 Subject: [PATCH 10/12] rc.lua: Add a modeline by default. This way people who fork the default config now wont have a bad suprise the day 5.0 gets released. --- awesomerc.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/awesomerc.lua b/awesomerc.lua index f02a0bc6a..00d4134a2 100644 --- a/awesomerc.lua +++ b/awesomerc.lua @@ -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") From 9cbc8a18c04c89f40aa208ed5596163e7aeac47a Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 2 Feb 2020 21:03:15 -0500 Subject: [PATCH 11/12] rc.lua: Revert moving the sloppy focus to awful.permissions. This caused a behavior change it wasn't clear how to use the permission API to change the focus mode. The change will only take effect if the user override the API level. --- awesomerc.lua | 5 +++++ lib/awful/autofocus.lua | 13 ++++++++----- lib/awful/permissions/init.lua | 11 ++++++++--- spec/awful/permissions_spec.lua | 1 + 4 files changed, 22 insertions(+), 8 deletions(-) diff --git a/awesomerc.lua b/awesomerc.lua index 00d4134a2..c91243957 100644 --- a/awesomerc.lua +++ b/awesomerc.lua @@ -538,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) diff --git a/lib/awful/autofocus.lua b/lib/awful/autofocus.lua index c11d4831e..a765ea6be 100644 --- a/lib/awful/autofocus.lua +++ b/lib/awful/autofocus.lua @@ -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 diff --git a/lib/awful/permissions/init.lua b/lib/awful/permissions/init.lua index c1baa0c56..d0c82d1e9 100644 --- a/lib/awful/permissions/init.lua +++ b/lib/awful/permissions/init.lua @@ -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 diff --git a/spec/awful/permissions_spec.lua b/spec/awful/permissions_spec.lua index ce13aabcd..f02fde363 100644 --- a/spec/awful/permissions_spec.lua +++ b/spec/awful/permissions_spec.lua @@ -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 = { From d0187055d8088d40205b7f9487a8ab5c5f66ecf5 Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Tue, 4 Feb 2020 02:13:03 -0500 Subject: [PATCH 12/12] init: Print a warning when non-ASCII encoding is used in a modeline. --- options.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/options.c b/options.c index bfc3af56a..7ff357202 100644 --- a/options.c +++ b/options.c @@ -122,8 +122,15 @@ options_init_config(char *execpath, char *configpath, int *init_flags, string_ar /* 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) + 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: