From 2341fc3b8cb34958892869b1f92994f3fe570878 Mon Sep 17 00:00:00 2001 From: Daniel Colascione Date: Fri, 3 Jan 2025 23:36:55 -0500 Subject: [PATCH] Automatically redraw frames for filtered faces Automatically redraw frames when we detect that a window parameter used in a face filter is changed. (Bug#75291) * src/window.c (Fset_window_parameter): redraw window, actually whole frame in this implementation, on face-relevant parameter change (syms_of_window): add window_auto_redraw_on_parameter_change * src/xfaces.c (evaluate_face_filter): record properties (cherry picked from commit b828d36deded145ebbd3419f618cf52f7862c4c8) --- src/window.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ src/xfaces.c | 3 +++ 2 files changed, 50 insertions(+) diff --git a/src/window.c b/src/window.c index 5a10c381eaf..17bbe213e05 100644 --- a/src/window.c +++ b/src/window.c @@ -2394,8 +2394,41 @@ Return VALUE. */) { register struct window *w = decode_any_window (window); Lisp_Object old_alist_elt; + struct frame* f; old_alist_elt = Fassq (parameter, w->window_parameters); + + /* If this window parameter has been used in a face remapping filter + expression anywhere at any time and we changed its value, force a + from-scratch redisplay to make sure that everything that depends on + a face filtered on the window parameter value is up-to-date. + + We compare with Qt here instead of using !NILP so that users can + set this property to a non-nil, non-t value to inhibit this + mechanism for a specific window parameter. + + FIXME: instead of taking a sledgehammer to redisplay, we could be + more precise in tracking which display bits depend on which + remapped faces. In particular, 1) if a window parameter named in a + face filter affects only faces used in drawing fringes, we don't + need to redraw TTY frames, but if the filter is ever used in a + non-fringe context (e.g. the 'face' text property), we need to + redraw TTY frames too. 2) In the fringe case, we should limit the + redraw damage to the fringes of the affected window and not the + whole frame containing the window. Today, we seldom change window + parameters named in face filters. We should implement the + optimizations above when this assumption no longer holds. */ + if (SYMBOLP (parameter) + && WINDOW_LIVE_P (window) + && EQ (Fget (parameter, QCfiltered), Qt) + && FRAME_WINDOW_P ((f = WINDOW_XFRAME (w))) + && !EQ (CDR_SAFE (old_alist_elt), value) + && window_auto_redraw_on_parameter_change) + { + f->face_change = 1; + fset_redisplay (f); + } + if (NILP (old_alist_elt)) wset_window_parameters (w, Fcons (Fcons (parameter, value), w->window_parameters)); @@ -9071,6 +9104,20 @@ Elisp for testing purposes only. */); window_dead_windows_table = CALLN (Fmake_hash_table, QCweakness, Qt); + DEFVAR_BOOL ("window-auto-redraw-on-parameter-change", + window_auto_redraw_on_parameter_change, + doc: /* When non-nil, redraw based on face filters. +When this variable is non-nil, force a potentially expensive redraw when +a window parameter named in a `:window' expression for ':filtered' +changes. This redraw is necessary for correctness; this variable is an +escape hatch to recover performance in the case that our assumption that +these parameter changes are rare does not hold. + +You can also inhibit the automatic redraw for a specific window +parameter by setting the `:filtered` symbol property of the parameter +name to `'ignore'. */); + window_auto_redraw_on_parameter_change = true; + defsubr (&Sselected_window); defsubr (&Sold_selected_window); defsubr (&Sminibuffer_window); diff --git a/src/xfaces.c b/src/xfaces.c index d1ca2e0d5d4..75fe73154ca 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -2512,6 +2512,9 @@ evaluate_face_filter (Lisp_Object filter, struct window *w, if (!NILP (filter)) goto err; + if (NILP (Fget (parameter, QCfiltered))) + Fput (parameter, QCfiltered, Qt); + bool match = false; if (w) { -- 2.39.5