options: improve usage message printing

Laying the usage message out by hand is tedious, also error prone
because the option names are duplicated at 2 places and have to be
consistent.

Create a struct to hold the option names and help messages, and
do layout programmatically.

Signed-off-by: Yuxuan Shui <yshuiv7@gmail.com>
This commit is contained in:
Yuxuan Shui
2022-08-21 15:07:44 +01:00
committed by Arda Atci
parent 0004173ecd
commit fbe7ed5699

View File

@@ -7,6 +7,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
#include <xcb/render.h> // for xcb_render_fixed_t, XXX
@@ -20,6 +22,222 @@
#pragma GCC diagnostic error "-Wunused-parameter"
struct picom_option {
const char *long_name;
int has_arg;
int val;
const char *arg_name;
const char *help;
};
// clang-format off
static const struct option *longopts = NULL;
static const struct picom_option picom_options[] = {
#ifdef CONFIG_LIBCONFIG
{"config" , required_argument, 256, NULL , "Path to the configuration file."},
#endif
{"help" , no_argument , 'h', NULL , "Print this help message and exit."},
{"shadow-radius" , required_argument, 'r', NULL , "The blur radius for shadows. (default 12)"},
{"shadow-opacity" , required_argument, 'o', NULL , "The translucency for shadows. (default .75)"},
{"shadow-offset-x" , required_argument, 'l', NULL , "The left offset for shadows. (default -15)"},
{"shadow-offset-y" , required_argument, 't', NULL , "The top offset for shadows. (default -15)"},
{"fade-in-step" , required_argument, 'I', NULL , "Opacity change between steps while fading in. (default 0.028)"},
{"fade-out-step" , required_argument, 'O', NULL , "Opacity change between steps while fading out. (default 0.03)"},
{"fade-delta" , required_argument, 'D', NULL , "The time between steps in a fade in milliseconds. (default 10)"},
{"menu-opacity" , required_argument, 'm', NULL , "The opacity for menus. (default 1.0)"},
{"shadow" , no_argument , 'c', NULL , "Enabled client-side shadows on windows."},
{"clear-shadow" , no_argument , 'z', NULL , "Don't dreaw shadow behind the window."},
{"fading" , no_argument , 'f', NULL , "Fade windows in/out when opening/closing and when opacity changes, "
"unless --no-fading-openclose is used."},
{"inactive-opacity" , required_argument, 'i', NULL , "Opacity of inactive windows. (0.1 - 1.0)"},
{"frame-opacity" , required_argument, 'e', NULL , "Opacity of window titlebars and borders. (0.1 - 1.0)"},
{"daemon" , no_argument , 'b', NULL , "Daemonize process."},
{"shadow-red" , required_argument, 257, NULL , "Red color value of shadow (0.0 - 1.0, defaults to 0)."},
{"shadow-green" , required_argument, 258, NULL , "Green color value of shadow (0.0 - 1.0, defaults to 0)."},
{"shadow-blue" , required_argument, 259, NULL , "Blue color value of shadow (0.0 - 1.0, defaults to 0)."},
{"inactive-opacity-override" , no_argument , 260, NULL , "Inactive opacity set by -i overrides value of _NET_WM_WINDOW_OPACITY."},
{"inactive-dim" , required_argument, 261, NULL , "Dim inactive windows. (0.0 - 1.0, defaults to 0)"},
{"mark-wmwin-focused" , no_argument , 262, NULL , "Try to detect WM windows and mark them as active."},
{"shadow-exclude" , required_argument, 263, NULL , "Exclude conditions for shadows."},
{"mark-ovredir-focused" , no_argument , 264, NULL , "Mark windows that have no WM frame as active."},
{"no-fading-openclose" , no_argument , 265, NULL , "Do not fade on window open/close."},
{"shadow-ignore-shaped" , no_argument , 266, NULL , "Do not paint shadows on shaped windows. (Deprecated, use --shadow-exclude "
"\'bounding_shaped\' or --shadow-exclude \'bounding_shaped && "
"!rounded_corners\' instead.)"},
{"detect-rounded-corners" , no_argument , 267, NULL , "Try to detect windows with rounded corners and don't consider them shaped "
"windows. Affects --shadow-ignore-shaped, --unredir-if-possible, and "
"possibly others. You need to turn this on manually if you want to match "
"against rounded_corners in conditions."},
{"detect-client-opacity" , no_argument , 268, NULL , "Detect _NET_WM_WINDOW_OPACITY on client windows, useful for window "
"managers not passing _NET_WM_WINDOW_OPACITY of client windows to frame"},
{"refresh-rate" , required_argument, 269, NULL , NULL},
{"vsync" , optional_argument, 270, NULL , "Enable VSync"},
{"sw-opti" , no_argument , 274, NULL , NULL},
{"vsync-aggressive" , no_argument , 275, NULL , NULL},
{"use-ewmh-active-win" , no_argument , 276, NULL , "Use _NET_WM_ACTIVE_WINDOW on the root window to determine which window is "
"focused instead of using FocusIn/Out events"},
{"respect-prop-shadow" , no_argument , 277, NULL , NULL},
{"unredir-if-possible" , no_argument , 278, NULL , "Unredirect all windows if a full-screen opaque window is detected, to "
"maximize performance for full-screen applications."},
{"focus-exclude" , required_argument, 279, "COND" , "Specify a list of conditions of windows that should always be considered focused."},
{"inactive-dim-fixed" , no_argument , 280, NULL , "Use fixed inactive dim value."},
{"detect-transient" , no_argument , 281, NULL , "Use WM_TRANSIENT_FOR to group windows, and consider windows in the same "
"group focused at the same time."},
{"detect-client-leader" , no_argument , 282, NULL , "Use WM_CLIENT_LEADER to group windows, and consider windows in the same group "
"focused at the same time. This usually means windows from the same application "
"will be considered focused or unfocused at the same time. WM_TRANSIENT_FOR has "
"higher priority if --detect-transient is enabled, too."},
{"blur-background" , no_argument , 283, NULL , "Blur background of semi-transparent / ARGB windows. May impact performance"},
{"blur-background-frame" , no_argument , 284, NULL , "Blur background of windows when the window frame is not opaque. Implies "
"--blur-background."},
{"blur-background-fixed" , no_argument , 285, NULL , "Use fixed blur strength instead of adjusting according to window opacity."},
#ifdef CONFIG_DBUS
{"dbus" , no_argument , 286, NULL , "Enable remote control via D-Bus. See the D-BUS API section in the man page "
"for more details."},
#endif
{"logpath" , required_argument, 287, NULL , NULL},
{"invert-color-include" , required_argument, 288, "COND" , "Specify a list of conditions of windows that should be painted with "
"inverted color."},
{"opengl" , no_argument , 289, NULL , NULL},
{"backend" , required_argument, 290, NULL , "Backend. Possible values are: xrender"
#ifdef CONFIG_OPENGL
", glx"
#endif
},
{"glx-no-stencil" , no_argument , 291, NULL , NULL},
{"benchmark" , required_argument, 293, NULL , "Benchmark mode. Repeatedly paint until reaching the specified cycles."},
{"benchmark-wid" , required_argument, 294, NULL , "Specify window ID to repaint in benchmark mode. If omitted or is 0, the whole"
" screen is repainted."},
{"blur-background-exclude" , required_argument, 296, "COND" , "Exclude conditions for background blur."},
{"active-opacity" , required_argument, 297, NULL , "Default opacity for active windows. (0.0 - 1.0)"},
{"glx-no-rebind-pixmap" , no_argument , 298, NULL , NULL},
{"glx-swap-method" , required_argument, 299, NULL , NULL},
{"fade-exclude" , required_argument, 300, "COND" , "Exclude conditions for fading."},
{"blur-kern" , required_argument, 301, NULL , "Specify the blur convolution kernel, see man page for more details"},
{"resize-damage" , required_argument, 302, NULL , NULL}, // only used by legacy backends
{"glx-use-gpushader4" , no_argument , 303, NULL , NULL},
{"opacity-rule" , required_argument, 304, "OPACITY:COND", "Specify a list of opacity rules, see man page for more details"},
{"shadow-exclude-reg" , required_argument, 305, NULL , NULL},
{"paint-exclude" , required_argument, 306, NULL , NULL},
{"xinerama-shadow-crop" , no_argument , 307, NULL , "Crop shadow of a window fully on a particular Xinerama screen to the screen."},
{"unredir-if-possible-exclude" , required_argument, 308, "COND" , "Conditions of windows that shouldn't be considered full-screen for "
"unredirecting screen."},
{"unredir-if-possible-delay" , required_argument, 309, NULL, "Delay before unredirecting the window, in milliseconds. Defaults to 0."},
{"write-pid-path" , required_argument, 310, "PATH" , "Write process ID to a file."},
{"vsync-use-glfinish" , no_argument , 311, NULL , NULL},
{"xrender-sync-fence" , no_argument , 313, NULL , "Additionally use X Sync fence to sync clients' draw calls. Needed on "
"nvidia-drivers with GLX backend for some users."},
{"show-all-xerrors" , no_argument , 314, NULL , NULL},
{"no-fading-destroyed-argb" , no_argument , 315, NULL , "Do not fade destroyed ARGB windows with WM frame. Workaround bugs in Openbox, "
"Fluxbox, etc."},
{"force-win-blend" , no_argument , 316, NULL , "Force all windows to be painted with blending. Useful if you have a custom "
"shader that could turn opaque pixels transparent."},
{"glx-fshader-win" , required_argument, 317, NULL , NULL},
{"version" , no_argument , 318, NULL , "Print version number and exit."},
{"no-x-selection" , no_argument , 319, NULL , NULL},
{"log-level" , required_argument, 321, NULL , "Log level, possible values are: trace, debug, info, warn, error"},
{"log-file" , required_argument, 322, NULL , "Path to the log file."},
{"use-damage" , no_argument , 323, NULL , "Render only the damaged (changed) part of the screen"},
{"no-use-damage" , no_argument , 324, NULL , "Disable the use of damage information. This cause the whole screen to be"
"redrawn everytime, instead of the part of the screen that has actually "
"changed. Potentially degrades the performance, but might fix some artifacts."},
{"no-vsync" , no_argument , 325, NULL , "Disable VSync"},
{"max-brightness" , required_argument, 326, NULL , "Dims windows which average brightness is above this threshold. Requires "
"--no-use-damage. (default: 1.0, meaning no dimming)"},
{"transparent-clipping" , no_argument , 327, NULL , "Make transparent windows clip other windows like non-transparent windows do, "
"instead of blending on top of them"},
{"transparent-clipping-exclude", required_argument, 338, "COND" , "Specify a list of conditions of windows that should never have "
"transparent clipping applied. Useful for screenshot tools, where you "
"need to be able to see through transparent parts of the window."},
{"blur-method" , required_argument, 328, NULL , "The algorithm used for background bluring. Available choices are: 'none' to "
"disable, 'gaussian', 'box' or 'kernel' for custom convolution blur with "
"--blur-kern. Note: 'gaussian' and 'box' is not supported by --legacy-backends."},
{"blur-size" , required_argument, 329, NULL , "The radius of the blur kernel for 'box' and 'gaussian' blur method."},
{"blur-deviation" , required_argument, 330, NULL , "The standard deviation for the 'gaussian' blur method."},
{"blur-strength" , required_argument, 331, NULL , "The strength level of the 'dual_kawase' blur method."},
{"shadow-color" , required_argument, 332, NULL , "Color of shadow, as a hex RGB string (defaults to #000000)"},
{"corner-radius" , required_argument, 333, NULL , "Sets the radius of rounded window corners. When > 0, the compositor will "
"round the corners of windows. (defaults to 0)."},
{"rounded-corners-exclude" , required_argument, 334, "COND" , "Exclude conditions for rounded corners."},
{"clip-shadow-above" , required_argument, 335, NULL , "Specify a list of conditions of windows to not paint a shadow over, such "
"as a dock window."},
{"window-shader-fg" , required_argument, 336, "PATH" , "Specify GLSL fragment shader path for rendering window contents. Does not"
" work when `--legacy-backends` is enabled. See man page for more details."},
{"window-shader-fg-rule" , required_argument, 337, "PATH:COND" , "Specify GLSL fragment shader path for rendering window contents using "
"patterns. Pattern should be in the format of SHADER_PATH:PATTERN, "
"similar to --opacity-rule. SHADER_PATH can be \"default\", in which case "
"the default shader will be used. Does not work when --legacy-backends is "
"enabled. See man page for more details"},
{"legacy-backends" , no_argument , 733, NULL , "Use deprecated version of the backends."},
{"monitor-repaint" , no_argument , 800, NULL , "Highlight the updated area of the screen. For debugging."},
{"diagnostics" , no_argument , 801, NULL , "Print diagnostic information"},
{"debug-mode" , no_argument , 802, NULL , "Render into a separate window, and don't take over the screen. Useful when "
"you want to attach a debugger to picom"},
{"no-ewmh-fullscreen" , no_argument , 803, NULL , "Do not use EWMH to detect fullscreen windows. Reverts to checking if a "
"window is fullscreen based only on its size and coordinates."},
};
// clang-format on
static void setup_longopts(void) {
auto opts = ccalloc(ARR_SIZE(picom_options) + 1, struct option);
for (size_t i = 0; i < ARR_SIZE(picom_options); i++) {
opts[i].name = picom_options[i].long_name;
opts[i].has_arg = picom_options[i].has_arg;
opts[i].flag = NULL;
opts[i].val = picom_options[i].val;
}
longopts = opts;
}
void print_help(const char *help, size_t indent, size_t curr_indent, size_t line_wrap,
FILE *f) {
if (curr_indent > indent) {
fputs("\n", f);
curr_indent = 0;
}
if (line_wrap - indent <= 1) {
line_wrap = indent + 2;
}
size_t pos = 0;
size_t len = strlen(help);
while (pos < len) {
fprintf(f, "%*s", (int)(indent - curr_indent), "");
curr_indent = 0;
size_t towrite = line_wrap - indent;
while (help[pos] == ' ') {
pos++;
}
if (pos + towrite > len) {
towrite = len - pos;
fwrite(help + pos, 1, towrite, f);
} else {
auto space_break = towrite;
while (space_break > 0 && help[pos + space_break - 1] != ' ') {
space_break--;
}
bool print_hyphen = false;
if (space_break == 0) {
print_hyphen = true;
towrite--;
} else {
towrite = space_break;
}
fwrite(help + pos, 1, towrite, f);
if (print_hyphen) {
fputs("-", f);
}
}
fputs("\n", f);
pos += towrite;
}
}
/**
* Print usage text.
*/
@@ -394,8 +612,58 @@ static void usage(const char *argv0, int ret) {
" case the default shader will be used. Does not work when\n"
" `--legacy-backends` is enabled.\n";
FILE *f = (ret ? stderr : stdout);
fprintf(f, usage_text, argv0);
#undef WARNING_DISABLED
fprintf(f, "picom (%s)\n", PICOM_VERSION);
fprintf(f, "Standalone X11 compositor\n");
fprintf(f, "Please report bugs to https://github.com/yshui/picom\n\n");
fprintf(f, "Usage: %s [OPTION]...\n\n", argv0);
fprintf(f, "OPTIONS:\n");
int line_wrap = 80;
struct winsize window_size = {0};
if (ioctl(fileno(f), TIOCGWINSZ, &window_size) != -1) {
line_wrap = window_size.ws_col;
}
size_t help_indent = 0;
for (size_t i = 0; i < ARR_SIZE(picom_options); i++) {
if (picom_options[i].help == NULL) {
// Hide options with no help message.
continue;
}
auto option_len = strlen(picom_options[i].long_name) + 2 + 4;
if (picom_options[i].arg_name) {
option_len += strlen(picom_options[i].arg_name) + 1;
}
if (option_len > help_indent && option_len < 30) {
help_indent = option_len;
}
}
help_indent += 6;
for (size_t i = 0; i < ARR_SIZE(picom_options); i++) {
if (picom_options[i].help == NULL) {
continue;
}
size_t option_len = 8;
fprintf(f, " ");
if ((picom_options[i].val > 'a' && picom_options[i].val < 'z') ||
(picom_options[i].val > 'A' && picom_options[i].val < 'Z')) {
fprintf(f, "-%c, ", picom_options[i].val);
} else {
fprintf(f, " ");
}
fprintf(f, "--%s", picom_options[i].long_name);
option_len += strlen(picom_options[i].long_name) + 2;
if (picom_options[i].arg_name) {
fprintf(f, "=%s", picom_options[i].arg_name);
option_len += strlen(picom_options[i].arg_name) + 1;
}
fprintf(f, " ");
option_len += 2;
print_help(picom_options[i].help, help_indent, option_len,
(size_t)line_wrap, f);
}
}
static const char *shortopts = "D:I:O:r:o:m:l:t:i:e:hscnfFCazGb";
@@ -512,6 +780,8 @@ static const struct option longopts[] = {
/// Return true if we should quit
bool get_early_config(int argc, char *const *argv, char **config_file, bool *all_xerrors,
bool *fork, int *exit_code) {
setup_longopts();
int o = 0, longopt_idx = -1;
// Pre-parse the commandline arguments to check for --config and invalid