diff --git a/.clang-tidy b/.clang-tidy index 57d71dc561..cb310eebc3 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -14,7 +14,6 @@ Checks: > -readability-magic-numbers, -readability-identifier-length, -bugprone-easily-swappable-parameters -AnalyzeTemporaryDtors: false FormatStyle: file CheckOptions: - key: readability-magic-numbers.IgnoredIntegerValues @@ -29,3 +28,5 @@ CheckOptions: value: true - key: bugprone-signed-char-misuse.CharTypdefsToIgnore value: int8_t + - key: bugprone-suspicious-string-compare.WarnOnLogicalNotComparison + value: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 1db934c05c..e5c6ed6e5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,22 @@ +# v12-rc3 (2024-Aug-30) + +## Bug fixes + +* `transparent-clipping` has no effect (#1317) +* `unredir` in window rules not being parsed correctly +* Changing window opacity with `picom-trans` does not take effect immediately (#1315) + +## Documentation + +* Document behavior change around rounded corners and fullscreen windows (#1323) + # v12-rc2 (2024-Aug-17) ## Bug fixes -* Setting corner-radius to 0 cause all windows to not render: #1311 +* Setting corner-radius to 0 cause all windows to not render (#1311) * Setting corner-radius causes windows to have a 1-pixel transparent border -* Window shaders no longer work: #1312 +* Window shaders no longer work (#1312) # v12-rc1 (2024-Aug-12) diff --git a/man/meson.build b/man/meson.build index a14e7608d8..58a2daa385 100644 --- a/man/meson.build +++ b/man/meson.build @@ -2,18 +2,32 @@ mans = ['picom.1', 'picom-inspect.1', 'picom-trans.1'] if get_option('with_docs') a2x = find_program('asciidoctor') foreach m : mans - custom_target(m, output: [m], input: [m+'.adoc'], - command: [a2x, '-a', - 'picom-version='+version, - '--backend', 'manpage', '@INPUT@', '-D', - meson.current_build_dir()], - install: true, - install_dir: join_paths(get_option('mandir'), 'man1')) - custom_target(m+'.html', output: [m+'.html'], input: [m+'.adoc'], - command: [a2x, '-a', - 'picom-version='+version, - '--backend', 'html', '@INPUT@', '-D', - meson.current_build_dir()], - install_dir: get_option('datadir') / 'doc' / 'picom') + custom_target( + m, + output: [m], + input: [m + '.adoc'], + command: [ + a2x, + '-a', 'picom-version=v' + meson.project_version(), + '--backend', 'manpage', + '@INPUT@', + '-D', meson.current_build_dir(), + ], + install: true, + install_dir: join_paths(get_option('mandir'), 'man1'), + ) + custom_target( + m + '.html', + output: [m + '.html'], + input: [m + '.adoc'], + command: [ + a2x, + '-a', 'picom-version=v' + meson.project_version(), + '--backend', 'html', + '@INPUT@', + '-D', meson.current_build_dir(), + ], + install_dir: get_option('datadir') / 'doc' / 'picom', + ) endforeach endif diff --git a/man/picom.1.adoc b/man/picom.1.adoc index d0e5946ea1..c95f179203 100644 --- a/man/picom.1.adoc +++ b/man/picom.1.adoc @@ -291,9 +291,9 @@ Following is a list of all the options that are superseded by window rules: <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <>, <>. As well as the xref:wintypes[*wintypes*] configuration file option. -If window rules option is used, none of the above options will have any effect. And warning messages will be issued. +If window rules option is used, none of the above options will have any effect. And warning messages will be issued. When the window rules option is used, the compositor will also behave somewhat differently in certain cases. One such case is that fullscreen windows will no longer have their rounded corners disabled by default. -If you are currently using some of these options and want to switch to window rules, see the xref:_migrating_old_rules[*Migrating old rules*] section for how to convert them. +If you are currently using some of these options and want to switch to window rules, or if you want to keep the existing behavior, see the xref:_migrating_old_rules[*Migrating old rules*] section for how to convert them. === Syntax @@ -362,6 +362,14 @@ Most of the rule options should 1:1 map to the new window rules. Here is a list *Active window*:: This includes option <>. This option was only used to influence what windows are considered active, to apply inactive opacity and dimming. Since with window rules you no longer need the compositor to help you decide what is active and what is not (see above), this option is no longer needed. +*Rounded corners and fullscreen windows*:: Rounded corners are no longer automatically disabled for fullscreen windows. If you want to disable rounded corners for fullscreen windows, you can use the following rule: ++ +---- +rules = ( + { match = "fullscreen"; corner-radius = 0; }, +) +---- + FORMAT OF CONDITIONS -------------------- Some options accept a condition string to match certain windows. A condition string is formed by one or more conditions, joined by logical operators. diff --git a/meson.build b/meson.build index 9b17f0d075..2cad574ab3 100644 --- a/meson.build +++ b/meson.build @@ -3,19 +3,33 @@ project('picom', 'c', version: '11', cc = meson.get_compiler('c') -# use project version by default -version = 'v'+meson.project_version() - # use git describe if that's available git = find_program('git', required: false) if git.found() - gitv = run_command('git', 'rev-parse', '--short=5', 'HEAD', check: false) + gitv = run_command('git', 'rev-parse', '--short=7', 'HEAD', check: false) if gitv.returncode() == 0 - version = 'vgit-'+gitv.stdout().strip() + commit_hash_short = gitv.stdout().strip() + endif + git_upstream = run_command('git', 'rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}', check: false) + if git_upstream.returncode() == 0 + remote = git_upstream.stdout().strip().split('/')[0] + else + remote = 'origin' + endif + git_repository = run_command('git', 'remote', 'get-url', remote, check: false) + if git_repository.returncode() == 0 + repository = git_repository.stdout().strip() endif endif -add_global_arguments('-DPICOM_VERSION="'+version+'"', language: 'c') +add_global_arguments('-DPICOM_VERSION="v'+meson.project_version()+'"', language: 'c') +if is_variable('repository') + add_global_arguments('-DPICOM_FULL_VERSION="v'+meson.project_version()+' ('+repository+' revision '+commit_hash_short+')"', language: 'c') +elif is_variable('commit_hash_short') + add_global_arguments('-DPICOM_FULL_VERSION="v'+meson.project_version()+' (revision '+commit_hash_short+')"', language: 'c') +else + add_global_arguments('-DPICOM_FULL_VERSION="v'+meson.project_version()+'"', language: 'c') +endif if get_option('buildtype') == 'release' add_global_arguments('-DNDEBUG', language: 'c') diff --git a/src/c2.c b/src/c2.c index 5f80f3e3fb..87fb0c63ab 100644 --- a/src/c2.c +++ b/src/c2.c @@ -24,7 +24,6 @@ #include "atom.h" #include "common.h" #include "compiler.h" -#include "config.h" #include "log.h" #include "test.h" #include "utils/str.h" @@ -645,10 +644,10 @@ c2_parse_group(const char *pattern, int offset, c2_condition_node_ptr *presult, } next_expected = true; - if (!mstrncmp("&&", pattern + offset)) { + if (mstrncmp("&&", pattern + offset) == 0) { ops[elei] = C2_B_OAND; ++offset; - } else if (!mstrncmp("||", pattern + offset)) { + } else if (mstrncmp("||", pattern + offset) == 0) { ops[elei] = C2_B_OOR; ++offset; } else { @@ -812,7 +811,7 @@ static int c2_parse_target(const char *pattern, int offset, c2_condition_node_pt // Check for predefined targets static const int npredefs = (int)(sizeof(C2_PREDEFS) / sizeof(C2_PREDEFS[0])); for (int i = 0; i < npredefs; ++i) { - if (!strcmp(C2_PREDEFS[i].name, pleaf->tgt)) { + if (strcmp(C2_PREDEFS[i].name, pleaf->tgt) == 0) { pleaf->predef = i; break; } @@ -1662,19 +1661,19 @@ static bool c2_string_op(const c2_condition_node_leaf *leaf, const char *target) } if (leaf->match_ignorecase) { switch (leaf->match) { - case C2_L_MEXACT: return !strcasecmp(target, leaf->ptnstr); + case C2_L_MEXACT: return strcasecmp(target, leaf->ptnstr) == 0; case C2_L_MCONTAINS: return strcasestr(target, leaf->ptnstr); case C2_L_MSTART: - return !strncasecmp(target, leaf->ptnstr, strlen(leaf->ptnstr)); + return strncasecmp(target, leaf->ptnstr, strlen(leaf->ptnstr)) == 0; case C2_L_MWILDCARD: return !fnmatch(leaf->ptnstr, target, FNM_CASEFOLD); default: unreachable(); } } else { switch (leaf->match) { - case C2_L_MEXACT: return !strcmp(target, leaf->ptnstr); + case C2_L_MEXACT: return strcmp(target, leaf->ptnstr) == 0; case C2_L_MCONTAINS: return strstr(target, leaf->ptnstr); case C2_L_MSTART: - return !strncmp(target, leaf->ptnstr, strlen(leaf->ptnstr)); + return strncmp(target, leaf->ptnstr, strlen(leaf->ptnstr)) == 0; case C2_L_MWILDCARD: return !fnmatch(leaf->ptnstr, target, 0); default: unreachable(); } diff --git a/src/config.h b/src/config.h index 8b504743e0..8ea4f07f61 100644 --- a/src/config.h +++ b/src/config.h @@ -499,18 +499,18 @@ bool parse_config(options_t *, const char *config_file); */ static inline attr_pure int parse_backend(const char *str) { for (int i = 0; BACKEND_STRS[i]; ++i) { - if (!strcasecmp(str, BACKEND_STRS[i])) { + if (strcasecmp(str, BACKEND_STRS[i]) == 0) { return i; } } // Keep compatibility with an old revision containing a spelling mistake... - if (!strcasecmp(str, "xr_glx_hybird")) { + if (strcasecmp(str, "xr_glx_hybird") == 0) { log_warn("backend xr_glx_hybird should be xr_glx_hybrid, the misspelt " "version will be removed soon."); return BKEND_XR_GLX_HYBRID; } // cju wants to use dashes - if (!strcasecmp(str, "xr-glx-hybrid")) { + if (strcasecmp(str, "xr-glx-hybrid") == 0) { log_warn("backend xr-glx-hybrid should be xr_glx_hybrid, the alternative " "version will be removed soon."); return BKEND_XR_GLX_HYBRID; diff --git a/src/config_libconfig.c b/src/config_libconfig.c index 5ac2d21e9d..dfebe017b8 100644 --- a/src/config_libconfig.c +++ b/src/config_libconfig.c @@ -539,7 +539,7 @@ static enum window_unredir_option parse_unredir_option(config_setting_t *setting return WINDOW_UNREDIR_INVALID; } if (strcmp(sval, "yes") == 0 || strcmp(sval, "true") == 0 || - strcmp(sval, "default") == 0 || strcmp(sval, "when-possible-else-terminate")) { + strcmp(sval, "default") == 0 || strcmp(sval, "when-possible-else-terminate") == 0) { return WINDOW_UNREDIR_WHEN_POSSIBLE_ELSE_TERMINATE; } if (strcmp(sval, "preferred") == 0 || strcmp(sval, "when-possible") == 0) { @@ -729,6 +729,16 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) { } config_set_auto_convert(&cfg, 1); + // --log-level + if (config_lookup_string(&cfg, "log-level", &sval)) { + opt->log_level = string_to_log_level(sval); + if (opt->log_level == LOG_LEVEL_INVALID) { + log_warn("Invalid log level, defaults to WARN"); + } else { + log_set_level_tls(opt->log_level); + } + } + // Get options from the configuration file. We don't do range checking // right now. It will be done later @@ -935,15 +945,6 @@ bool parse_config_libconfig(options_t *opt, const char *config_file) { goto out; } } - // --log-level - if (config_lookup_string(&cfg, "log-level", &sval)) { - opt->log_level = string_to_log_level(sval); - if (opt->log_level == LOG_LEVEL_INVALID) { - log_warn("Invalid log level, defaults to WARN"); - } else { - log_set_level_tls(opt->log_level); - } - } // --log-file if (config_lookup_string(&cfg, "log-file", &sval)) { if (*sval != '/') { diff --git a/src/dbus.c b/src/dbus.c index e0770d4671..18cdd410d8 100644 --- a/src/dbus.c +++ b/src/dbus.c @@ -593,7 +593,7 @@ cdbus_process_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_ } #define append(tgt, type, expr) \ - if (!strcmp(#tgt, target)) { \ + if (strcmp(#tgt, target) == 0) { \ if (!cdbus_append_##type(reply, (expr))) { \ return DBUS_HANDLER_RESULT_NEED_MEMORY; \ } \ @@ -609,7 +609,7 @@ cdbus_process_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_ append(Leader, wid_variant, wm_ref_win_id(wm_ref_leader(w->tree_ref))); append_win_property(Name, name, string_variant); - if (!strcmp("Type", target)) { + if (strcmp("Type", target) == 0) { DBusMessageIter iter, sub; dbus_message_iter_init_append(reply, &iter); if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "s", &sub)) { @@ -630,7 +630,7 @@ cdbus_process_window_property_get(session_t *ps, DBusMessage *msg, cdbus_window_ return DBUS_HANDLER_RESULT_HANDLED; } - if (!strcmp("Next", target)) { + if (strcmp("Next", target) == 0) { cdbus_window_t next_id = 0; auto below = wm_ref_below(cursor); if (below != NULL) { @@ -722,7 +722,7 @@ cdbus_process_win_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE } #define append_win_property(tgt, type) append(tgt, type, w->tgt) - if (!strcmp("next", target)) { + if (strcmp("next", target) == 0) { auto below = wm_ref_below(cursor); xcb_window_t next_id = below ? wm_ref_win_id(below) : XCB_NONE; if (!cdbus_append_wid(reply, next_id)) { @@ -808,15 +808,15 @@ cdbus_process_win_set(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBusE } bool changed = false; - if (!strcmp("shadow_force", target)) { + if (strcmp("shadow_force", target) == 0) { w->options_override.shadow = val == UNSET ? TRI_UNKNOWN : (val == ON ? TRI_TRUE : TRI_FALSE); changed = true; - } else if (!strcmp("fade_force", target)) { + } else if (strcmp("fade_force", target) == 0) { w->options_override.fade = val == UNSET ? TRI_UNKNOWN : (val == ON ? TRI_TRUE : TRI_FALSE); changed = true; - } else if (!strcmp("invert_color_force", target)) { + } else if (strcmp("invert_color_force", target) == 0) { w->options_override.invert_color = val == UNSET ? TRI_UNKNOWN : (val == ON ? TRI_TRUE : TRI_FALSE); changed = true; @@ -853,7 +853,7 @@ cdbus_process_find_win(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus xcb_window_t wid = XCB_NONE; - if (!strcmp("client", target)) { + if (strcmp("client", target) == 0) { // Find window by client window cdbus_window_t client = XCB_NONE; if (!cdbus_msg_get_arg(msg, 1, CDBUS_TYPE_WINDOW, &client)) { @@ -864,7 +864,7 @@ cdbus_process_find_win(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus if (w) { wid = wm_ref_win_id(w); } - } else if (!strcmp("focused", target)) { + } else if (strcmp("focused", target) == 0) { // Find focused window auto focused_win = wm_focused_win(ps->wm); auto w = wm_ref_deref(focused_win); @@ -899,7 +899,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus } #define append(tgt, type, ret) \ - if (!strcmp(#tgt, target)) { \ + if (strcmp(#tgt, target) == 0) { \ if (reply != NULL && !cdbus_append_##type(reply, ret)) { \ return DBUS_HANDLER_RESULT_NEED_MEMORY; \ } \ @@ -907,7 +907,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus } #define append_session_option(tgt, type) append(tgt, type, ps->o.tgt) - if (!strcmp("backend", target)) { + if (strcmp("backend", target) == 0) { assert(!ps->o.use_legacy_backends || (size_t)ps->o.legacy_backend < ARR_SIZE(BACKEND_STRS)); const char *name = ps->o.use_legacy_backends @@ -919,7 +919,7 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus return DBUS_HANDLER_RESULT_HANDLED; } - append(version, string, PICOM_VERSION); + append(version, string, PICOM_FULL_VERSION); append(pid, int32, getpid()); append(display, string, DisplayString(ps->c.dpy)); append(config_file, string, "Unknown"); @@ -983,9 +983,6 @@ cdbus_process_opts_get(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus return DBUS_HANDLER_RESULT_HANDLED; } -// XXX Remove this after header clean up -void queue_redraw(session_t *ps); - /** * Process a opts_set D-Bus request. */ @@ -1012,7 +1009,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus goto cdbus_process_opts_set_success; \ } - if (!strcmp("clear_shadow", target) || !strcmp("track_focus", target)) { + if (strcmp("clear_shadow", target) == 0 || strcmp("track_focus", target) == 0) { goto cdbus_process_opts_set_success; } @@ -1022,7 +1019,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus opts_set_do(no_fading_openclose, BOOLEAN, bool, val); opts_set_do(stoppaint_force, UINT32, cdbus_enum_t, val); - if (!strcmp("unredir_if_possible", target)) { + if (strcmp("unredir_if_possible", target) == 0) { dbus_bool_t val = FALSE; get_msg_arg(BOOLEAN, val); if (ps->o.unredir_if_possible != val) { @@ -1032,7 +1029,7 @@ cdbus_process_opts_set(session_t *ps, DBusMessage *msg, DBusMessage *reply, DBus goto cdbus_process_opts_set_success; } - if (!strcmp("redirected_force", target)) { + if (strcmp("redirected_force", target) == 0) { cdbus_enum_t val = UNSET; get_msg_arg(UINT32, val); if (ps->o.redirected_force != val) { diff --git a/src/diagnostic.c b/src/diagnostic.c index 7cfac5cbde..46c9d57652 100644 --- a/src/diagnostic.c +++ b/src/diagnostic.c @@ -13,7 +13,7 @@ #include "picom.h" void print_diagnostics(session_t *ps, const char *config_file, bool compositor_running) { - printf("**Version:** " PICOM_VERSION "\n"); + printf("**Version:** " PICOM_FULL_VERSION "\n"); // printf("**CFLAGS:** %s\n", "??"); printf("\n### Extensions:\n\n"); printf("* Shape: %s\n", ps->shape_exists ? "Yes" : "No"); diff --git a/src/options.c b/src/options.c index d917b5bb81..907cfb41cf 100644 --- a/src/options.c +++ b/src/options.c @@ -627,7 +627,7 @@ void print_help(const char *help, size_t indent, size_t curr_indent, size_t line */ static void usage(const char *argv0, int ret) { FILE *f = (ret ? stderr : stdout); - fprintf(f, "picom (%s)\n", PICOM_VERSION); + fprintf(f, "picom " PICOM_FULL_VERSION "\n"); fprintf(f, "Standalone X11 compositor\n"); fprintf(f, "Please report bugs to https://github.com/yshui/picom\n\n"); @@ -778,7 +778,7 @@ bool get_early_config(int argc, char *const *argv, char **config_file, bool *all } else if (o == 314) { *all_xerrors = true; } else if (o == 318) { - printf("%s\n", PICOM_VERSION); + printf(PICOM_FULL_VERSION "\n"); return true; } else if (o == 307) { // --plugin diff --git a/src/wm/win.c b/src/wm/win.c index d032f25742..ac2c1f215d 100644 --- a/src/wm/win.c +++ b/src/wm/win.c @@ -1822,20 +1822,22 @@ bool win_process_animation_and_state_change(struct session *ps, struct win *w, d // because its first damage event might come a bit late. bool will_never_render = (!w->ever_damaged || w->win_image == NULL) && w->state != WSTATE_MAPPED; + auto win_ctx = win_script_context_prepare(ps, w); + bool geometry_changed = !win_geometry_eq(w->previous.g, w->g); + auto old_state = w->previous.state; + + w->previous.state = w->state; + w->previous.opacity = w->opacity; + w->previous.g = w->g; + if (!ps->redirected || will_never_render) { // This window won't be rendered, so we don't need to run the animations. - bool state_changed = w->previous.state != w->state; - w->previous.state = w->state; - w->previous.opacity = w->opacity; + bool state_changed = old_state != w->state || + win_ctx.opacity_before != win_ctx.opacity || + geometry_changed; return state_changed || (w->running_animation_instance != NULL); } - auto win_ctx = win_script_context_prepare(ps, w); - w->previous.opacity = w->opacity; - - bool geometry_changed = !win_geometry_eq(w->previous.g, w->g); - w->previous.g = w->g; - // Try to determine the right animation trigger based on state changes. Note there // is some complications here. X automatically unmaps windows before destroying // them. So a "close" trigger will also be fired from a UNMAPPED -> DESTROYED @@ -1848,7 +1850,7 @@ bool win_process_animation_and_state_change(struct session *ps, struct win *w, d // Animation trigger priority: // state > geometry > opacity - if (w->previous.state != w->state) { + if (old_state != w->state) { // Send D-Bus signal if (ps->o.dbus) { switch (w->state) { @@ -1864,8 +1866,6 @@ bool win_process_animation_and_state_change(struct session *ps, struct win *w, d } } - auto old_state = w->previous.state; - w->previous.state = w->state; switch (WSTATE_PAIR(old_state, w->state)) { case WSTATE_PAIR(WSTATE_UNMAPPED, WSTATE_MAPPED): trigger = w->in_openclose ? ANIMATION_TRIGGER_OPEN