commit 0a5e69eaef780e66a88bb6eca4e369b5e337245b Author: Stefan Monnier Date: Thu May 28 17:36:19 2026 -0400 lisp/visual-wrap.el (visual-wrap--content-prefix): Adjust doc Suggested by Andrea Alberti . diff --git a/lisp/visual-wrap.el b/lisp/visual-wrap.el index 733f059ca85..e7be0c960d6 100644 --- a/lisp/visual-wrap.el +++ b/lisp/visual-wrap.el @@ -56,7 +56,7 @@ extra indent = -1 Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna - aliqua. Ut enim ad minim veniam, quis nostrud exercitation + aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. actual indent = 2 @@ -64,7 +64,7 @@ extra indent = 2 Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna - aliqua. Ut enim ad minim veniam, quis nostrud exercitation + aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat." :type 'integer :safe #'integerp @@ -174,7 +174,6 @@ members of `visual-wrap--safe-display-specs' (which see)." (defun visual-wrap--content-prefix (prefix) "Get the next-line prefix for the specified first-line PREFIX. -POSITION is the position in the buffer where PREFIX is located. This returns a string prefix to use for subsequent lines; a number, indicating the pixel width to use for whitespace alignment; or nil if commit 7ee33143981199184e8b12db12ed7d0a26b947f5 Author: Eli Zaretskii Date: Thu May 28 18:36:11 2026 +0300 Speed-up cursor motion under 'display-line-numbers-mode' * src/xfaces.c (Finternal_lisp_face_equal_p): Accept an additional optional argument INHERIT; if non-nil, consider two faces equal if one inherits from the other and doesn't specify any other attributes to be different from it. * src/xdisp.c (try_cursor_movement, try_window_id): Call 'Finternal_lisp_face_equal_p' with non-nil INHERIT argument, to speed up redisplay in the default case when line numbers are shown. (Bug#81133) * test/src/xfaces-tests.el (xfaces-test-face-equality): New test. diff --git a/src/xdisp.c b/src/xdisp.c index 1691bd0d80e..dabe63b902a 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -19856,7 +19856,7 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp, && !(!NILP (Vdisplay_line_numbers) && NILP (Finternal_lisp_face_equal_p (Qline_number, Qline_number_current_line, - w->frame))) + w->frame, Qt))) /* This code is not used for mini-buffer for the sake of the case of redisplaying to replace an echo area message; since in that case the mini-buffer contents per se are usually @@ -22657,7 +22657,7 @@ try_window_id (struct window *w) || (!NILP (Vdisplay_line_numbers) && NILP (Finternal_lisp_face_equal_p (Qline_number, Qline_number_current_line, - w->frame)))) + w->frame, Qt)))) GIVE_UP (24); /* composition-break-at-point is incompatible with the optimizations diff --git a/src/xfaces.c b/src/xfaces.c index 73d475c757f..fcec00fbc1f 100644 --- a/src/xfaces.c +++ b/src/xfaces.c @@ -4527,12 +4527,16 @@ lface_equal_p (Lisp_Object *v1, Lisp_Object *v2) DEFUN ("internal-lisp-face-equal-p", Finternal_lisp_face_equal_p, - Sinternal_lisp_face_equal_p, 2, 3, 0, + Sinternal_lisp_face_equal_p, 2, 4, 0, doc: /* True if FACE1 and FACE2 are equal. If the optional argument FRAME is given, report on FACE1 and FACE2 in that frame. If FRAME is t, report on the defaults for FACE1 and FACE2 (for new frames). -If FRAME is omitted or nil, use the selected frame. */) - (Lisp_Object face1, Lisp_Object face2, Lisp_Object frame) +If FRAME is omitted or nil, use the selected frame. +Optional fourth argument INHERIT, if non-nil, means the faces +are considered equal if one inherits from the other in a way +that makes them have the same attributes when used on display. */) + (Lisp_Object face1, Lisp_Object face2, Lisp_Object frame, + Lisp_Object inherit) { bool equal_p; struct frame *f; @@ -4548,6 +4552,45 @@ If FRAME is omitted or nil, use the selected frame. */) lface2 = lface_from_face_name (f, face2, true); equal_p = lface_equal_p (XVECTOR (lface1)->contents, XVECTOR (lface2)->contents); + if (!(NILP (inherit) || equal_p)) + { + /* The below is a subset of merging the descendant face with its + parent(s). We only consider a direct inheritance (so no FACE1 + that inherits from some other face which inherits from FACE2), + and the values of :inherit that are lists are not considered. + This is enough in simple cases such as the line-number-current + face that inherits from line-number. */ + Lisp_Object attrs1[LFACE_VECTOR_SIZE], attrs2[LFACE_VECTOR_SIZE]; + int i; + equal_p = true; + memcpy (attrs1, xvector_contents (lface1), sizeof attrs1); + memcpy (attrs2, xvector_contents (lface2), sizeof attrs2); + /* If either face inherits from the other one, and all the other + face attributes of the inheriting face are either unspecified + or equal to those of the parent face, consider the faces equal. */ + if (EQ (attrs1[LFACE_INHERIT_INDEX], face2)) + { + for (i = 1; i < LFACE_VECTOR_SIZE && equal_p; ++i) + { + if (i == LFACE_INHERIT_INDEX) + continue; + equal_p = face_attr_equal_p (attrs1[i], attrs2[i]) + || UNSPECIFIEDP (attrs1[i]); + } + } + else if (EQ (attrs2[LFACE_INHERIT_INDEX], face1)) + { + for (i = 1; i < LFACE_VECTOR_SIZE && equal_p; ++i) + { + if (i == LFACE_INHERIT_INDEX) + continue; + equal_p = face_attr_equal_p (attrs1[i], attrs2[i]) + || UNSPECIFIEDP (attrs2[i]); + } + } + + } + return equal_p ? Qt : Qnil; } diff --git a/test/src/xfaces-tests.el b/test/src/xfaces-tests.el index ae57909a664..e79d2e88543 100644 --- a/test/src/xfaces-tests.el +++ b/test/src/xfaces-tests.el @@ -65,6 +65,16 @@ 'button)))) (kill-buffer buf))) +(ert-deftest xfaces-test-face-equality () + "Test `internal-lisp-face-equal-p'." + (should (internal-lisp-face-equal-p 'default 'default)) + (should-not (internal-lisp-face-equal-p 'default 'region)) + (should-not (internal-lisp-face-equal-p 'line-number 'default)) + (should-not (internal-lisp-face-equal-p 'line-number ' + line-number-current-line)) + (should (internal-lisp-face-equal-p 'line-number + 'line-number-current-line t))) + (provide 'xfaces-tests) ;;; xfaces-tests.el ends here commit 833553dd9aec0072961a7f1a7797f9481855a07f Author: Michael Albinus Date: Thu May 28 10:03:05 2026 +0200 dbus-call-method-asynchronously supports also an ERROR-HANDLER * doc/misc/dbus.texi (Asynchronous Methods): HANDLER can also be (HANDLER . ERROR-HANDLER). * etc/NEWS: Mention ERROR-HANDLER of dbus-call-method-asynchronously. * lisp/net/dbus.el (dbus-call-method-asynchronously): Adapt docstring. (dbus-check-event, dbus-handle-event): HANDLER can also be (HANDLER . ERROR-HANDLER). * src/dbusbind.c (Fdbus_message_internal): HANDLER can also be (HANDLER . ERROR-HANDLER). (Bug#80952) * test/lisp/net/dbus-tests.el (dbus--test-method-another-handler) (dbus--test-method-error-handler): New defvars. (dbus--test-method-another-handler) (dbus--test-method-error-handler): New functions. (dbus-test04-call-method-error-handler): New test. (dbus-test10-keep-fd): Extend test. diff --git a/doc/misc/dbus.texi b/doc/misc/dbus.texi index 8764fcade90..d63e26755d9 100644 --- a/doc/misc/dbus.texi +++ b/doc/misc/dbus.texi @@ -1340,9 +1340,20 @@ keyword @code{:session}. D-Bus object path, @var{service} is registered at. @var{interface} is an interface offered by @var{service}. It must provide @var{method}. -@var{handler} is a Lisp function, which is called when the -corresponding return message arrives. If @var{handler} is @code{nil}, -no return message will be expected. +@var{handler} is a Lisp function, which is called when the corresponding +return message has arrived. It uses the returned values from the +@var{method} call as arguments. These are the same arguments which are +returned when @code{dbus-call-method} is invoked instead, +@pxref{Synchronous Methods}. If @var{handler} is @code{nil}, no return +message will be expected. + +@var{handler} can also be the cons cell @code{(@var{handler} +. @var{error-handler})}. In this case, @var{error-handler} will be +called in case an error is returned from D-Bus. It uses the returned +D-Bus error as argument. + +Neither the return value of @var{handler} nor the return value of +@var{error-handler} is used. If the parameter @code{:timeout} is given, the following integer @var{timeout} specifies the maximum number of milliseconds before a @@ -1366,19 +1377,40 @@ arguments. They are converted into D-Bus types as described in If @var{handler} is a Lisp function, the function returns a key into the hash table @code{dbus-registered-objects-table}. The corresponding entry in the hash table is removed, when the return -message arrives, and @var{handler} is called. Example: +message arrives, and @var{handler} is called. Examples: + +The return value of @samp{org.freedesktop.portal.Settings.ReadOne} is a variant. @lisp (dbus-call-method-asynchronously - :system "org.freedesktop.Hal" - "/org/freedesktop/Hal/devices/computer" - "org.freedesktop.Hal.Device" "GetPropertyString" - (lambda (msg) (message "%s" msg)) - "system.kernel.machine") + :session "org.freedesktop.portal.Desktop" + "/org/freedesktop/portal/desktop" + "org.freedesktop.portal.Settings" "ReadOne" + '((lambda (msg) (message "Method handler %s" msg)) . + (lambda (err) (message "Error handler %s" err))) + "org.freedesktop.appearance" "color-scheme") + +@print{} Method handler (0) + +@result{} (:serial :session 4) +@end lisp -@print{} i686 +There does not exist a method @samp{org.freedesktop.portal.Settings.ReadTwo}. -@result{} (:serial :system 2) +@lisp +(dbus-call-method-asynchronously + :session "org.freedesktop.portal.Desktop" + "/org/freedesktop/portal/desktop" + "org.freedesktop.portal.Settings" "ReadTwo" + '((lambda (msg) (message "Method handler %s" msg)) . + (lambda (err) (message "Error handler %s" err))) + "org.freedesktop.appearance" "color-scheme") + +@print{} Error handler + (dbus-error "org.freedesktop.DBus.Error.UnknownMethod + No such method "ReadTwo") + +@result{} (:serial :session 5) @end lisp @end defun diff --git a/etc/NEWS b/etc/NEWS index e6fd8a7f747..a5806a99e31 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -69,6 +69,14 @@ Emacs 30. It allows Lisp programs that present completion candidates ("completion frontends") to provide additional information which can be used to adjust or optimize completion candidates computation. +** D-Bus + ++++ +*** Support error handler in asynchronous method calls. +The HANDLER argument of 'dbus-call-method-asynchronously' can be a cons +cell '(HANDLER . ERROR-HANDLER)'. ERROR-HANDLER is invoked if the +method call returns with a D-Bus error; the error is passed as argument. + * Changes in Emacs 32.1 on Non-Free Operating Systems diff --git a/lisp/net/dbus.el b/lisp/net/dbus.el index 0c748e76fcf..3a5cf48b92f 100644 --- a/lisp/net/dbus.el +++ b/lisp/net/dbus.el @@ -445,8 +445,17 @@ object path SERVICE is registered at. INTERFACE is an interface offered by SERVICE. It must provide METHOD. HANDLER is a Lisp function, which is called when the corresponding -return message has arrived. If HANDLER is nil, no return message -will be expected. +return message has arrived. It uses the returned values from the METHOD +call as arguments. These are the same arguments which are returned when +`dbus-call-method' is invoked instead. If HANDLER is nil, no return +message will be expected. + +HANDLER can also be the cons cell `(HANDLER . ERROR-HANDLER)'. In this +case, ERROR-HANDLER will be called in case an error is returned from +D-Bus. It uses the returned D-Bus error as argument. + +Neither the return value of HANDLER nor the return value of +ERROR-HANDLER is used. If the parameter `:timeout' is given, the following integer TIMEOUT specifies the maximum number of milliseconds before the @@ -477,18 +486,37 @@ about type keywords, see Info node `(dbus)Type Conversion'. If HANDLER is a Lisp function, the function returns a key into the hash table `dbus-registered-objects-table'. The corresponding entry in the hash table is removed, when the return message arrives, -and HANDLER is called. +and HANDLER is called. Examples: -Example: +The return value of \"org.freedesktop.portal.Settings.ReadOne\" is a variant. \(dbus-call-method-asynchronously - :system \"org.freedesktop.Hal\" \"/org/freedesktop/Hal/devices/computer\" - \"org.freedesktop.Hal.Device\" \"GetPropertyString\" #\\='message - \"system.kernel.machine\") + :session \"org.freedesktop.portal.Desktop\" + \"/org/freedesktop/portal/desktop\" + \"org.freedesktop.portal.Settings\" \"ReadOne\" + \\='((lambda (msg) (message \"Method handler %s\" msg)) . + (lambda (err) (message \"Error handler %s\" err))) + \"org.freedesktop.appearance\" \"color-scheme\") + + -| Method handler (0) - -| i686 + => (:serial :session 4) - => (:serial :system 2)" +There does not exist a method \"org.freedesktop.portal.Settings.ReadTwo\". + +\(dbus-call-method-asynchronously + :session \"org.freedesktop.portal.Desktop\" + \"/org/freedesktop/portal/desktop\" + \"org.freedesktop.portal.Settings\" \"ReadTwo\" + \\='((lambda (msg) (message \"Method handler %s\" msg)) . + (lambda (err) (message \"Error handler %s\" err))) + \"org.freedesktop.appearance\" \"color-scheme\") + + -| Error handler + (dbus-error org.freedesktop.DBus.Error.UnknownMethod + No such method \"ReadTwo\") + + => (:serial :session 5)" (or (featurep 'dbusbind) (signal 'dbus-error (list "Emacs not compiled with dbus support"))) @@ -504,6 +532,7 @@ Example: (or (stringp method) (signal 'wrong-type-argument (list 'stringp method))) (or (null handler) (functionp handler) + (and (listp handler) (functionp (car handler)) (functionp (cdr handler))) (signal 'wrong-type-argument (list 'functionp handler))) (apply #'dbus-message-internal dbus-message-type-method-call @@ -1111,9 +1140,11 @@ INTERFACE and MEMBER denote the message which has been sent. When TYPE is `dbus-message-type-error', MEMBER is the error name. HANDLER is the function which has been registered for this -message. ARGS are the typed arguments as returned from the -message. They are passed to HANDLER without type information, -when it is called during event handling in `dbus-handle-event'. +message. It can also be a cons cell (HANDLER . ERROR-HANDLER). + +ARGS are the typed arguments as returned from the message. They are +passed to HANDLER without type information, when it is called during +event handling in `dbus-handle-event'. This function signals a `dbus-error' if the event is not well formed." @@ -1150,7 +1181,10 @@ formed." (or (= dbus-message-type-method-return (nth 2 event)) (stringp (nth 8 event))) ;; Handler. - (functionp (nth 9 event)) + (or (functionp (nth 9 event)) + (and (consp (nth 9 event)) + (functionp (car (nth 9 event))) + (functionp (cdr (nth 9 event))))) ;; Arguments. (listp (nthcdr 10 event))) (signal 'dbus-error (list "Not a valid D-Bus event" event)))) @@ -1207,10 +1241,17 @@ If the HANDLER returns a `dbus-error', it is propagated as return message." (setq result (dbus-ignore-errors (apply (nth 9 event) args))) ;; Error messages must be propagated. The error name is in ;; the member slot. - (when (= dbus-message-type-error (nth 2 event)) - (signal 'dbus-error (cons (nth 8 event) args))) - ;; Apply the handler. - (setq result (apply (nth 9 event) args)) + (let* ((handler (nth 9 event)) + (error-handler (if (functionp handler) #'signal + (prog1 (cdr handler) + (setq handler (car handler)))))) + (setq result + (if (= dbus-message-type-error (nth 2 event)) + (funcall + error-handler + (cons 'dbus-error (cons (nth 8 event) args))) + ;; Apply the handler. + (apply handler args)))) ;; Return an (error) message when it is a message call. (when (= dbus-message-type-method-call (nth 2 event)) (dbus-ignore-errors diff --git a/src/dbusbind.c b/src/dbusbind.c index 95fedeb166b..7039eac3dbe 100644 --- a/src/dbusbind.c +++ b/src/dbusbind.c @@ -1411,7 +1411,11 @@ usage: (dbus-message-internal &rest REST) */) XD_DBUS_VALIDATE_PATH (path); XD_DBUS_VALIDATE_INTERFACE (interface); XD_DBUS_VALIDATE_MEMBER (member); - if (!NILP (handler) && !FUNCTIONP (handler)) + if (!NILP (handler) + && !(FUNCTIONP (handler) + || (CONSP (handler) + && FUNCTIONP (CAR_SAFE (handler)) + && FUNCTIONP (CDR_SAFE (handler))))) wrong_type_argument (Qinvalid_function, handler); } @@ -1562,6 +1566,12 @@ usage: (dbus-message-internal &rest REST) */) if (mtype != DBUS_MESSAGE_TYPE_METHOD_CALL) XD_SIGNAL1 (build_string (":keep-fd is only supported on method calls")); + /* This is because the error handler and the keepfd path use + the same slot in Vdbus_registered_objects_table. */ + if (CONSP (handler)) + XD_SIGNAL1 + (build_string + (":keep-fd cannot be used when there is an error handler")); /* Ignore this keyword if unsupported. */ #ifdef DBUS_TYPE_UNIX_FD @@ -1842,9 +1852,6 @@ xd_read_message_1 (DBusConnection *connection, Lisp_Object bus) /* Remove the entry. */ Fremhash (key, Vdbus_registered_objects_table); - /* Store the event. */ - xd_store_event (CONSP (value) ? CAR_SAFE (value) : value, args, event_args); - #ifdef DBUS_TYPE_UNIX_FD /* Check, whether there is a file descriptor to be kept. value is (handler . path) @@ -1857,8 +1864,12 @@ xd_read_message_1 (DBusConnection *connection, Lisp_Object bus) Fcons (Fcons (CAR_SAFE (CDR_SAFE (CAR_SAFE (args))), CDR_SAFE (value)), xd_registered_fds); + value = CAR_SAFE (value); } #endif + + /* Store the event. */ + xd_store_event (value, args, event_args); } else /* DBUS_MESSAGE_TYPE_METHOD_CALL, DBUS_MESSAGE_TYPE_SIGNAL. */ @@ -2141,8 +2152,9 @@ means a wildcard then. OBJECT is either the handler to be called when a D-Bus message, which matches the key criteria, arrives (TYPE `:method', `:signal' and -`:monitor'), or a list (ACCESS EMITS-SIGNAL VALUE) for TYPE -`:property'. +`:monitor'), or a list (ACCESS EMITS-SIGNAL VALUE) for TYPE `:property'. +For type `:message', the handler slot can also be a cons cell (HANDLER +. ERROR-HANDLER) or (HANDLER . KEEP-FD-PATH). For entries of type `:signal' or `:monitor', there is also a fifth element RULE, which keeps the match string the signal or monitor is diff --git a/test/lisp/net/dbus-tests.el b/test/lisp/net/dbus-tests.el index 3d0ab522d3f..c8ff2941f3c 100644 --- a/test/lisp/net/dbus-tests.el +++ b/test/lisp/net/dbus-tests.el @@ -842,6 +842,73 @@ Returns the respective error." dbus--test-interface "Foo" :authorizable t "foo") :type 'dbus-error))) +(defvar dbus--test-method-another-handler nil) +(defun dbus--test-method-another-handler (&rest args) + "Method handler for `dbus-test04-call-method-error-handler'." + (should args) + (setq dbus--test-method-another-handler t)) + +(defvar dbus--test-method-error-handler nil) +(defun dbus--test-method-error-handler (&rest args) + "Error handler for `dbus-test04-call-method-error-handler'." + (should (eq 'dbus-error (caar args))) + (setq dbus--test-method-error-handler t)) + +(ert-deftest dbus-test04-call-method-error-handler () + "Verify `dbus-call-method-asynchronously' error handler." + :tags '(:expensive-test) + (skip-unless dbus--test-enabled-session-bus) + (dbus-ignore-errors (dbus-unregister-service :session dbus--test-service)) + (dbus-register-service :session dbus--test-service) + + (unwind-protect + (let ((method "Method") + (method-handler #'dbus--test-method-handler) + (handler #'dbus--test-method-another-handler) + (error-handler #'dbus--test-method-error-handler) + ;dbus-debug ; There would be errors otherwise. + registered) + + ;; Register. + (should + (equal + (setq + registered + (dbus-register-method + :session dbus--test-service dbus--test-path + dbus--test-interface method method-handler)) + `((:method :session ,dbus--test-interface ,method) + (,dbus--test-service ,dbus--test-path ,method-handler)))) + + ;; Call HANDLER. + (setq dbus--test-method-another-handler nil) + (dbus-call-method-asynchronously + :session dbus--test-service dbus--test-path + dbus--test-interface method `(,handler . ,error-handler) "foo") + (with-timeout (1 (dbus--test-timeout-handler)) + (while (not dbus--test-method-another-handler) + (read-event nil nil 0.1))) + (should dbus--test-method-another-handler) + + ;; Call ERROR-HANDLER. + (setq dbus--test-method-error-handler nil) + (dbus-call-method-asynchronously + :session dbus--test-service dbus--test-path + dbus--test-interface method `(,handler . ,error-handler) + "foo" "foo" "foo") + (with-timeout (1 (dbus--test-timeout-handler)) + (while (not dbus--test-method-error-handler) + (read-event nil nil 0.1))) + (should dbus--test-method-error-handler) + + ;; Unregister method. + (should (dbus-unregister-object registered)) + (should-not (dbus-unregister-object registered))) + + ;; Cleanup. + (ignore-errors (kill-buffer "*Warnings*")) + (dbus-unregister-service :session dbus--test-service))) + (defvar dbus--test-event-expected nil "The expected event in `dbus--test-signal-handler'.") @@ -2416,7 +2483,15 @@ The argument EXPECTED-ARGS is a list of expected arguments for the method." ;; Closing them again is a noop. (should-not (dbus--fd-close lock1)) - (should-not (dbus--fd-close lock2)))) + (should-not (dbus--fd-close lock2)) + + ;; `:keep-fd' cannot be used together with an error handler. + (should-error + (dbus-call-method-asynchronously + :system dbus--test-systemd-service dbus--test-systemd-path + dbus--test-systemd-manager-interface "Inhibit" + '(ignore . ignore) :keep-fd what who why mode) + :type 'dbus-error))) (ert-deftest dbus-test10-open-close-fd () "Check D-Bus open/close a file descriptor."