From 2dc218849dea256f5d48e2347fbfb8f2fead0aed Mon Sep 17 00:00:00 2001 From: Maxim Solovyov Date: Fri, 5 Jul 2024 20:43:09 +0300 Subject: [PATCH 1/4] vblank: fix a memory leak in the sgi_video_sync_thread function --- src/vblank.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vblank.c b/src/vblank.c index b7281360ee..78cb3bf547 100644 --- a/src/vblank.c +++ b/src/vblank.c @@ -229,6 +229,7 @@ static void *sgi_video_sync_thread(void *data) { pthread_mutex_unlock(&args->start_mtx); cleanup: + log_deinit_tls(); if (dpy) { glXMakeCurrent(dpy, None, NULL); if (ctx) { From 5da5a93dccefb526ad3779b87c86fa21cab2926b Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Mon, 15 Jul 2024 11:22:31 +0100 Subject: [PATCH 2/4] flake: update and upgrade to llvm 18 Signed-off-by: Yuxuan Shui --- flake.lock | 6 +++--- flake.nix | 20 ++++++++++++++------ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/flake.lock b/flake.lock index 9e50f9bfca..23a2f7fedd 100644 --- a/flake.lock +++ b/flake.lock @@ -41,11 +41,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1719397110, - "narHash": "sha256-rYGcPSy8hBx/0OJvHrtR50KGN9AR7GN/zXG4xOi5Dnc=", + "lastModified": 1721287717, + "narHash": "sha256-i5F24BL4FaJCOE0twnIPaltgDNeA44CqLsj/TqBAIsQ=", "owner": "nixos", "repo": "nixpkgs", - "rev": "c414599dfae92540ccc0c7d5d49f6cf6dc5b7fc8", + "rev": "56375296f413158b095ce493799cc8d237d70739", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 0663802827..c305f9bfa2 100644 --- a/flake.nix +++ b/flake.nix @@ -48,14 +48,19 @@ (final: prev: { stdenv = prev.withCFlags "-fno-omit-frame-pointer" prev.stdenv; }) + (final: prev: { + llvmPackages_18 = prev.llvmPackages_18 // { + stdenv = final.withCFlags "-fno-omit-frame-pointer" prev.llvmPackages_18.stdenv; + }; + }) ]; }; overlays = [overlay]; mkDevShell = p: p.overrideAttrs (o: { nativeBuildInputs = o.nativeBuildInputs ++ (with pkgs; [ - clang-tools_17 - llvmPackages_17.clang-unwrapped.python + clang-tools_18 + llvmPackages_18.clang-unwrapped.python python ]); hardeningDisable = ["fortify"]; @@ -72,15 +77,18 @@ overlay overlays ; - defaultPackage = pkgs.picom; - devShells.default = mkDevShell defaultPackage; + packages = { + default = pkgs.picom; + llvm = profilePkgs.llvm_18; + }; + devShells.default = mkDevShell packages.default; devShells.useClang = devShells.default.override { - inherit (pkgs.llvmPackages_17) stdenv; + inherit (pkgs.llvmPackages_18) stdenv; }; # build picom and all dependencies with frame pointer, making profiling/debugging easier. # WARNING! many many rebuilds devShells.useClangProfile = (mkDevShell profilePkgs.picom).override { - stdenv = profilePkgs.withCFlags "-fno-omit-frame-pointer" profilePkgs.llvmPackages_17.stdenv; + stdenv = profilePkgs.withCFlags "-fno-omit-frame-pointer" profilePkgs.llvmPackages_18.stdenv; }; }); } From c7fc8784d5bd1845a74c49d1424ca73251a49a83 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Mon, 29 Jul 2024 17:54:02 +0100 Subject: [PATCH 3/4] core: recheck focus when client windows change A toplevel is marked active if its client window is focused. So when a toplevel's client window changes, focus must be rechecked. Fix the problem that no window is marked active from the point when picom is just started, until the first focus change. Signed-off-by: Yuxuan Shui --- src/common.h | 2 ++ src/event.c | 49 ++++++++++++++++++++++++++++++++----------------- src/event.h | 1 + src/picom.c | 1 + 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/common.h b/src/common.h index 635fb9733b..94b0ac2709 100644 --- a/src/common.h +++ b/src/common.h @@ -224,6 +224,8 @@ typedef struct session { struct render_statistics render_stats; // === Operation related === + /// Whether there is a pending quest to get the focused window + bool pending_focus_check; /// Flags related to the root window uint64_t root_flags; /// Program options. diff --git a/src/event.c b/src/event.c index b2e9558ea5..7289a2bdcd 100644 --- a/src/event.c +++ b/src/event.c @@ -205,6 +205,7 @@ update_ewmh_active_win(struct x_connection * /*c*/, struct x_async_request_base auto ps = ((struct ev_ewmh_active_win_request *)req_base)->ps; free(req_base); + ps->pending_focus_check = false; if (reply_or_error->response_type == 0) { log_error("Failed to get _NET_ACTIVE_WINDOW: %s", x_strerror(((xcb_generic_error_t *)reply_or_error))); @@ -247,6 +248,8 @@ static void recheck_focus(struct x_connection * /*c*/, struct x_async_request_ba auto ps = ((struct ev_ewmh_active_win_request *)req_base)->ps; free(req_base); + ps->pending_focus_check = false; + // Determine the currently focused window so we can apply appropriate // opacity on it if (reply_or_error->response_type == 0) { @@ -288,18 +291,39 @@ static void recheck_focus(struct x_connection * /*c*/, struct x_async_request_ba } } +void ev_update_focused(struct session *ps) { + if (ps->pending_focus_check) { + return; + } + + if (ps->o.use_ewmh_active_win) { + auto req = ccalloc(1, struct ev_ewmh_active_win_request); + req->base.sequence = + xcb_get_property(ps->c.c, 0, ps->c.screen_info->root, + ps->atoms->a_NET_ACTIVE_WINDOW, XCB_ATOM_WINDOW, 0, 1) + .sequence; + req->base.callback = update_ewmh_active_win; + req->ps = ps; + x_await_request(&ps->c, &req->base); + log_debug("Started async request to get _NET_ACTIVE_WINDOW"); + } else { + auto req = ccalloc(1, struct ev_recheck_focus_request); + req->base.sequence = xcb_get_input_focus(ps->c.c).sequence; + req->base.callback = recheck_focus; + req->ps = ps; + x_await_request(&ps->c, &req->base); + log_debug("Started async request to recheck focus"); + } + + ps->pending_focus_check = true; +} + static inline void ev_focus_change(session_t *ps) { if (ps->o.use_ewmh_active_win) { // Not using focus_in/focus_out events. return; } - - auto req = ccalloc(1, struct ev_recheck_focus_request); - req->base.sequence = xcb_get_input_focus(ps->c.c).sequence; - req->base.callback = recheck_focus; - req->ps = ps; - x_await_request(&ps->c, &req->base); - log_debug("Started async request to recheck focus"); + ev_update_focused(ps); } static inline void ev_focus_in(session_t *ps, xcb_focus_in_event_t *ev) { @@ -556,16 +580,7 @@ static inline void ev_property_notify(session_t *ps, xcb_property_notify_event_t if (ps->c.screen_info->root == ev->window) { if (ps->o.use_ewmh_active_win && ps->atoms->a_NET_ACTIVE_WINDOW == ev->atom) { - auto req = ccalloc(1, struct ev_ewmh_active_win_request); - req->base.sequence = - xcb_get_property(ps->c.c, 0, ps->c.screen_info->root, - ps->atoms->a_NET_ACTIVE_WINDOW, - XCB_ATOM_WINDOW, 0, 1) - .sequence; - req->base.callback = update_ewmh_active_win; - req->ps = ps; - x_await_request(&ps->c, &req->base); - log_debug("Started async request to get _NET_ACTIVE_WINDOW"); + ev_update_focused(ps); } else { // Destroy the root "image" if the wallpaper probably changed if (x_is_root_back_pixmap_atom(ps->atoms, ev->atom)) { diff --git a/src/event.h b/src/event.h index 675b4d8c10..f4815b3b53 100644 --- a/src/event.h +++ b/src/event.h @@ -7,3 +7,4 @@ #include "common.h" void ev_handle(session_t *ps, xcb_generic_event_t *ev); +void ev_update_focused(struct session *ps); diff --git a/src/picom.c b/src/picom.c index a49c91295a..9d0e734401 100644 --- a/src/picom.c +++ b/src/picom.c @@ -1590,6 +1590,7 @@ static void handle_new_windows(session_t *ps) { wm_ref_win_id(wm_change.toplevel), wm_change.client.new_.x); } + ev_update_focused(ps); break; case WM_TREE_CHANGE_TOPLEVEL_RESTACKED: invalidate_reg_ignore(ps); break; default: unreachable(); From 76af408a64c45b2e7960daaf955f1b85a5980c73 Mon Sep 17 00:00:00 2001 From: Yuxuan Shui Date: Mon, 29 Jul 2024 21:40:12 +0100 Subject: [PATCH 4/4] win: process window flags in two stages Previously a window's leader changing will trigger factor change on all windows in the same window group. However, if a window in this group has already been iterated over in the foreach loop in `refresh_windows`, their flags will not be processed again in the same loop. This will cause those windows to have outdated per-window options. This commit split flag handling in two stages, factor changes are only processed after other flags of ALL windows have been processed. Signed-off-by: Yuxuan Shui --- src/picom.c | 12 ++++++++-- src/wm/defs.h | 3 +++ src/wm/win.c | 62 ++++++++++++++++++++++++++++++--------------------- src/wm/win.h | 9 +++++++- 4 files changed, 58 insertions(+), 28 deletions(-) diff --git a/src/picom.c b/src/picom.c index 9d0e734401..2377c1cfc1 100644 --- a/src/picom.c +++ b/src/picom.c @@ -1601,9 +1601,17 @@ static void handle_new_windows(session_t *ps) { static void refresh_windows(session_t *ps) { wm_stack_foreach(ps->wm, cursor) { auto w = wm_ref_deref(cursor); - if (w != NULL) { - win_process_update_flags(ps, w); + if (w == NULL) { + continue; + } + win_process_primary_flags(ps, w); + } + wm_stack_foreach(ps->wm, cursor) { + auto w = wm_ref_deref(cursor); + if (w == NULL) { + continue; } + win_process_secondary_flags(ps, w); } } diff --git a/src/wm/defs.h b/src/wm/defs.h index 801aea6efe..7c2b8421c7 100644 --- a/src/wm/defs.h +++ b/src/wm/defs.h @@ -52,6 +52,9 @@ enum win_flags { WIN_FLAGS_PIXMAP_STALE = 1, /// there was an error binding the window pixmap WIN_FLAGS_PIXMAP_ERROR = 4, + /// Window is damaged, and should be added to the damage region + /// (only used by the legacy backends, remove) + WIN_FLAGS_DAMAGED = 8, /// the client window needs to be updated WIN_FLAGS_CLIENT_STALE = 32, /// the window is mapped by X, we need to call map_win_start for it diff --git a/src/wm/win.c b/src/wm/win.c index 02e6bcf33c..d62aa24a02 100644 --- a/src/wm/win.c +++ b/src/wm/win.c @@ -103,11 +103,9 @@ win_get_leader_property(struct x_connection *c, struct atom *atoms, xcb_window_t static struct wm_ref *win_get_leader_raw(session_t *ps, struct win *w, int recursions); -/** - * Get the leader of a window. - * - * This function updates w->cache_leader if necessary. - */ +/// Get the leader of a window. +/// +/// This function updates w->cache_leader if necessary. static inline struct wm_ref *win_get_leader(session_t *ps, struct win *w) { return win_get_leader_raw(ps, w, 0); } @@ -172,13 +170,7 @@ static inline void group_on_factor_change(session_t *ps, struct wm_ref *leader) } } -/** - * Return whether a window group is really focused. - * - * @param leader leader window ID - * @return true if the window group is focused, false otherwise - */ -static inline bool group_is_focused(session_t *ps, struct wm_ref *leader) { +static inline bool win_is_group_focused_inner(session_t *ps, struct wm_ref *leader) { if (!leader) { return false; } @@ -198,6 +190,15 @@ static inline bool group_is_focused(session_t *ps, struct wm_ref *leader) { return false; } +bool win_is_group_focused(session_t *ps, struct win *w) { + if (w->state != WSTATE_MAPPED) { + return false; + } + + auto leader = win_get_leader(ps, w); + return win_is_group_focused_inner(ps, leader); +} + /** * Set leader of a window. */ @@ -214,7 +215,7 @@ static inline void win_set_leader(session_t *ps, struct win *w, xcb_window_t nle } auto i = wm_ref_deref(cursor); if (i != NULL) { - i->cache_leader = XCB_NONE; + i->cache_leader = NULL; } } @@ -434,8 +435,9 @@ static void win_update_properties(session_t *ps, struct win *w) { win_clear_all_properties_stale(w); } -/// Handle non-image flags. This phase might set IMAGES_STALE flags -void win_process_update_flags(session_t *ps, struct win *w) { +/// Handle primary flags. These flags are set as direct results of raw X11 window data +/// changes. +void win_process_primary_flags(session_t *ps, struct win *w) { log_trace("Processing flags for window %#010x (%s), was rendered: %d, flags: " "%#" PRIx64, win_id(w), w->name, w->to_paint, w->flags); @@ -451,7 +453,6 @@ void win_process_update_flags(session_t *ps, struct win *w) { return; } - bool damaged = false; if (win_check_flags_all(w, WIN_FLAGS_CLIENT_STALE)) { win_on_client_update(ps, w); win_clear_flags(w, WIN_FLAGS_CLIENT_STALE); @@ -490,12 +491,12 @@ void win_process_update_flags(session_t *ps, struct win *w) { ps->o.shadow_offset_y, ps->o.shadow_radius); win_update_bounding_shape(&ps->c, w, ps->shape_exists, ps->o.detect_rounded_corners); - damaged = true; win_clear_flags(w, WIN_FLAGS_SIZE_STALE); // Window shape/size changed, invalidate the images we built // log_trace("free out dated pict"); - win_set_flags(w, WIN_FLAGS_PIXMAP_STALE | WIN_FLAGS_FACTOR_CHANGED); + win_set_flags(w, WIN_FLAGS_PIXMAP_STALE | + WIN_FLAGS_FACTOR_CHANGED | WIN_FLAGS_DAMAGED); win_release_mask(ps->backend_data, w); win_release_shadow(ps->backend_data, w); @@ -505,8 +506,8 @@ void win_process_update_flags(session_t *ps, struct win *w) { } if (win_check_flags_all(w, WIN_FLAGS_POSITION_STALE)) { - damaged = true; win_clear_flags(w, WIN_FLAGS_POSITION_STALE); + win_set_flags(w, WIN_FLAGS_DAMAGED); } } @@ -514,6 +515,17 @@ void win_process_update_flags(session_t *ps, struct win *w) { win_update_properties(ps, w); win_clear_flags(w, WIN_FLAGS_PROPERTY_STALE); } +} + +/// Handle secondary flags. These flags are set during the processing of primary flags. +/// Flags are separated into primaries and secondaries because processing of secondary +/// flags must happen after primary flags of ALL windows are processed, to make sure some +/// global states (e.g. active window group) are consistent because they will be used in +/// the processing of secondary flags. +void win_process_secondary_flags(session_t *ps, struct win *w) { + if (w->flags == 0 || w->state != WSTATE_MAPPED) { + return; + } // Factor change flags could be set by previous stages, so must be handled // last @@ -521,11 +533,11 @@ void win_process_update_flags(session_t *ps, struct win *w) { win_on_factor_change(ps, w); win_clear_flags(w, WIN_FLAGS_FACTOR_CHANGED); } - - // Add damage, has to be done last so the window has the latest geometry - // information. - if (damaged) { + if (win_check_flags_all(w, WIN_FLAGS_DAMAGED)) { + // Add damage, has to be done last so the window has the latest geometry + // information. add_damage_from_win(ps, w); + win_clear_flags(w, WIN_FLAGS_DAMAGED); } } @@ -1376,7 +1388,7 @@ struct win *win_maybe_allocate(session_t *ps, struct wm_ref *cursor, .mode = WMODE_TRANS, .leader = XCB_NONE, - .cache_leader = XCB_NONE, + .cache_leader = NULL, .window_type = WINTYPE_UNKNOWN, .opacity_prop = OPAQUE, .opacity_set = 1, @@ -1628,7 +1640,7 @@ static void win_on_focus_change(session_t *ps, struct win *w) { } // If the group get unfocused, remove it from active_leader else if (!win_is_focused_raw(w) && leader && leader == active_leader && - !group_is_focused(ps, leader)) { + !win_is_group_focused_inner(ps, leader)) { wm_set_active_leader(ps->wm, XCB_NONE); group_on_factor_change(ps, leader); } diff --git a/src/wm/win.h b/src/wm/win.h index 9935b11f30..1f4417bab7 100644 --- a/src/wm/win.h +++ b/src/wm/win.h @@ -340,7 +340,8 @@ static const struct script_output_info win_script_outputs[] = { /// state change. bool win_process_animation_and_state_change(struct session *ps, struct win *w, double delta_t); double win_animatable_get(const struct win *w, enum win_script_output output); -void win_process_update_flags(session_t *ps, struct win *w); +void win_process_primary_flags(session_t *ps, struct win *w); +void win_process_secondary_flags(session_t *ps, struct win *w); void win_process_image_flags(session_t *ps, struct win *w); /// Start the unmap of a window. We cannot unmap immediately since we might need to fade @@ -421,6 +422,12 @@ void win_destroy_finish(session_t *ps, struct win *w); */ bool attr_pure win_is_focused_raw(const struct win *w); +/// Return whether the group a window belongs to is really focused. +/// +/// @param leader leader window ID +/// @return true if the window group is focused, false otherwise +bool win_is_group_focused(session_t *ps, struct win *w); + /// check if window has ARGB visual bool attr_pure win_has_alpha(const struct win *w);