commit 57b7c07b3709d9a2b2c70e0c8218a52448368a12 (HEAD, refs/remotes/origin/master) Author: Gerd Möllmann Date: Sat Jan 25 07:54:56 2025 +0100 Multi-tty: selected frame can be a GUI frame * src/dispnew.c (frame_selected_window_frame): New function. (is_cursor_obscured): Use it. (terminal_cursor_magic): Use the frame of the selected window of he root frame. (combine_updates_for_frame): Move Android conditional compilation out. diff --git a/src/dispnew.c b/src/dispnew.c index 5a8064a1a02..bc37230d70c 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -3906,26 +3906,44 @@ is_in_matrix (struct frame *f, int x, int y) return true; } -/* Is the terminal cursor of the selected frame obscured by a child - frame? */ +/* Return the frame of the selected window of frame F. + Value is NULL if we can't tell. */ + +static struct frame * +frame_selected_window_frame (struct frame *f) +{ + /* Paranoia. It should not happen that window or frame not valid. */ + Lisp_Object frame; + if (WINDOWP (f->selected_window) + && (frame = XWINDOW (f->selected_window)->frame, + FRAMEP (frame))) + return XFRAME (frame); + return NULL; +} + +/* Is the terminal cursor of ROOT obscured by a child frame? */ static bool -is_cursor_obscured (void) +is_cursor_obscured (struct frame *root) { + /* Determine in which frame on ROOT the cursor could be. */ + struct frame *sf = frame_selected_window_frame (root); + if (sf == NULL) + return false; + /* Give up if we can't tell where the cursor currently is. */ int x, y; - if (!abs_cursor_pos (SELECTED_FRAME (), &x, &y)) + if (!abs_cursor_pos (sf, &x, &y)) return false; /* (x, y) may be outside of the root frame in case the selected frame is a child frame which is clipped. */ - struct frame *root = root_frame (SELECTED_FRAME ()); if (!is_in_matrix (root, x, y)) return true; struct glyph_row *cursor_row = MATRIX_ROW (root->current_matrix, y); struct glyph *cursor_glyph = cursor_row->glyphs[0] + x; - return cursor_glyph->frame != SELECTED_FRAME (); + return cursor_glyph->frame != sf; } /* Decide where to show the cursor, and whether to hide it. @@ -3939,7 +3957,7 @@ static void terminal_cursor_magic (struct frame *root, struct frame *topmost_child) { /* By default, prevent the cursor "shining through" child frames. */ - if (is_cursor_obscured ()) + if (is_cursor_obscured (root)) tty_hide_cursor (FRAME_TTY (root)); /* If the terminal cursor is not in the topmost child, the topmost @@ -3947,7 +3965,8 @@ terminal_cursor_magic (struct frame *root, struct frame *topmost_child) non-nil, display the cursor in this "non-selected" topmost child frame to compensate for the fact that we can't display a non-selected cursor like on a window system frame. */ - if (topmost_child != SELECTED_FRAME ()) + struct frame *sf = frame_selected_window_frame (root); + if (sf && topmost_child != sf) { Lisp_Object frame; XSETFRAME (frame, topmost_child); @@ -3959,25 +3978,23 @@ terminal_cursor_magic (struct frame *root, struct frame *topmost_child) if (is_in_matrix (root, x, y)) { cursor_to (root, y, x); - tty_show_cursor (FRAME_TTY (topmost_child)); + tty_show_cursor (FRAME_TTY (root)); } else tty_hide_cursor (FRAME_TTY (root)); - } + } } } -#endif /* !HAVE_ANDROID */ - void combine_updates_for_frame (struct frame *f, bool inhibit_scrolling) { -#ifndef HAVE_ANDROID struct frame *root = root_frame (f); /* Determine visible frames on the root frame, including the root frame itself. Note that there are cases, see bug#75056, where we - can be called for invisible frames. */ + can be called for invisible frames. This looks like a bug with + multi-tty, but the old update code didn't check visibility either. */ Lisp_Object z_order = frames_in_reverse_z_order (root, true); if (NILP (z_order)) { @@ -4018,9 +4035,15 @@ combine_updates_for_frame (struct frame *f, bool inhibit_scrolling) add_frame_display_history (f, false); #endif } -#endif /* HAVE_ANDROID */ } +#else /* HAVE_ANDROID */ +void +combine_updates_for_frame (struct frame *f, bool inhibit_scrolling) +{ +} +#endif /* HAVE_ANDROID */ + /* Update on the screen all root frames ROOTS. Called from redisplay_internal as the last step of redisplaying. */ commit f37559a1ee035d184bc4db6e4f597281d3ac6879 Author: Gerd Möllmann Date: Sat Jan 25 07:33:45 2025 +0100 Fix mouse position handling for nested tty child frames * src/dispnew.c (child_xy): New function. * src/dispextern.h: Declare it. * src/term.c (tty_frame_at): Return child-relative (x, y) in output parameters. (Ftty_frame_at): Return a list (FRAME CHILD-X CHILD-Y). (handle_one_term_event): Adapt use of tty_frame_at. * lisp/xt-mouse.el (xterm-mouse-event): Use new tty-frame-at. diff --git a/lisp/xt-mouse.el b/lisp/xt-mouse.el index 19d688e4d1e..e395723d05c 100644 --- a/lisp/xt-mouse.el +++ b/lisp/xt-mouse.el @@ -298,16 +298,11 @@ which is the \"1006\" extension implemented in Xterm >= 277." ;; FIXME: The test for running in batch mode is here solely ;; for the sake of xt-mouse-tests where the only frame is ;; the initial frame. - (frame (unless noninteractive (tty-frame-at x y))) - ;;(_ (message (format "*** %S" frame))) - (frame-pos (frame-position frame)) - ;;(_ (message (format "*** %S" frame-pos))) - (x (- x (car frame-pos))) - (y (- y (cdr frame-pos))) - ;;(_ (message (format "*** %S %S" x y))) + (frame-and-xy (unless noninteractive (tty-frame-at x y))) + (frame (nth 0 frame-and-xy)) + (x (nth 1 frame-and-xy)) + (y (nth 2 frame-and-xy)) (w (window-at x y frame)) - ;;(_ (message (format "*** %S" w))) - (ltrb (window-edges w)) (left (nth 0 ltrb)) (top (nth 1 ltrb)) diff --git a/src/dispextern.h b/src/dispextern.h index 9c193e79fd1..e1214128e35 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -3959,6 +3959,7 @@ void combine_updates_for_frame (struct frame *f, bool inhibit_id_p); void tty_raise_lower_frame (struct frame *f, bool raise); int max_child_z_order (struct frame *parent); void root_xy (struct frame *f, int x, int y, int *rx, int *ry); +void child_xy (struct frame *f, int x, int y, int *cx, int *cy); bool is_frame_ancestor (struct frame *f1, struct frame *f2); INLINE_HEADER_END diff --git a/src/dispnew.c b/src/dispnew.c index 00e59c767e8..5a8064a1a02 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -3328,6 +3328,17 @@ root_xy (struct frame *f, int x, int y, int *rx, int *ry) } } +/* Translate absolute coordinates (X, Y) to coordinates relative to F's origin. */ + +void +child_xy (struct frame *f, int x, int y, int *cx, int *cy) +{ + int rx, ry; + root_xy (f, 0, 0, &rx, &ry); + *cx = x - rx; + *cy = y - ry; +} + /* Return the rectangle frame F occupies. X and Y are in absolute coordinates. */ diff --git a/src/term.c b/src/term.c index 7397ee68347..e13089af2bb 100644 --- a/src/term.c +++ b/src/term.c @@ -2595,7 +2595,7 @@ tty_draw_row_with_mouse_face (struct window *w, struct glyph_row *row, #endif static Lisp_Object -tty_frame_at (int x, int y) +tty_frame_at (int x, int y, int *cx, int *cy) { for (Lisp_Object frames = Ftty_frame_list_z_order (Qnil); !NILP (frames); @@ -2606,24 +2606,33 @@ tty_frame_at (int x, int y) int fx, fy; root_xy (f, 0, 0, &fx, &fy); - if (fx <= x && x < fx + f->pixel_width - && fy <= y && y < fy + f->pixel_height) - return frame; + if ((fx <= x && x < fx + f->pixel_width) + && (fy <= y && y < fy + f->pixel_height)) + { + child_xy (XFRAME (frame), x, y, cx, cy); + return frame; + } } return Qnil; } -DEFUN ("tty-frame-at", Ftty_frame_at, Stty_frame_at, - 2, 2, 0, - doc: /* Return tty frame containing pixel position X, Y. */) +DEFUN ("tty-frame-at", Ftty_frame_at, Stty_frame_at, 2, 2, 0, + doc : /* Return tty frame containing absolute pixel position (X, Y). +Value is nil if no frame found. Otherwise it is a list (FRAME CX CY), +where FRAME is the frame containing (X, Y) and CX and CY are X and Y +relative to FRAME. */) (Lisp_Object x, Lisp_Object y) { if (! FIXNUMP (x) || ! FIXNUMP (y)) /* Coordinates this big can not correspond to any frame. */ return Qnil; - return tty_frame_at (XFIXNUM (x), XFIXNUM (y)); + int cx, cy; + Lisp_Object frame = tty_frame_at (XFIXNUM (x), XFIXNUM (y), &cx, &cy); + if (NILP (frame)) + return Qnil; + return list3 (frame, make_fixnum (cx), make_fixnum (cy)); } #ifdef HAVE_GPM @@ -2756,11 +2765,9 @@ term_mouse_click (struct input_event *result, Gpm_Event *event, int handle_one_term_event (struct tty_display_info *tty, const Gpm_Event *event_in) { - Lisp_Object frame = tty_frame_at (event_in->x, event_in->y); - struct frame *f = decode_live_frame (frame); Gpm_Event event = *event_in; - event.x -= f->left_pos; - event.y -= f->top_pos; + Lisp_Object frame = tty_frame_at (event_in->x, event_in->y, &event.x, &event.y); + struct frame *f = decode_live_frame (frame); struct input_event ie; int count = 0; commit 9693f2a95ad14bf0caa34e60d72722d4dd4d85c9 Author: Gerd Möllmann Date: Sat Jan 25 06:43:49 2025 +0100 Fix tty-frame-at for nested tty child frames * src/term.c (tty_frame_at): Translate child frame origin to absolute coordinates. diff --git a/src/term.c b/src/term.c index 00bc94e6e31..7397ee68347 100644 --- a/src/term.c +++ b/src/term.c @@ -2603,9 +2603,11 @@ tty_frame_at (int x, int y) { Lisp_Object frame = Fcar (frames); struct frame *f = XFRAME (frame); + int fx, fy; + root_xy (f, 0, 0, &fx, &fy); - if (f->left_pos <= x && x < f->left_pos + f->pixel_width && - f->top_pos <= y && y < f->top_pos + f->pixel_height) + if (fx <= x && x < fx + f->pixel_width + && fy <= y && y < fy + f->pixel_height) return frame; } commit 8cbb3c7335ff03b6fae4efeae126e91993cb962a Author: Gerd Möllmann Date: Sat Jan 25 05:44:03 2025 +0100 Fix cursor positioning of nested tty child frames * src/terminal.c (cursor_to, raw_cursor_to): Fix translation to root coordinates. diff --git a/src/terminal.c b/src/terminal.c index db6d42d4b4f..e6d5a5d309a 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -110,9 +110,13 @@ set_terminal_window (struct frame *f, int size) void cursor_to (struct frame *f, int vpos, int hpos) { - if (FRAME_TERMINAL (f)->cursor_to_hook) - (*FRAME_TERMINAL (f)->cursor_to_hook) (f, vpos + f->top_pos, - hpos + f->left_pos); + struct terminal *term = FRAME_TERMINAL (f); + if (term->cursor_to_hook) + { + int x, y; + root_xy (f, hpos, vpos, &x, &y); + term->cursor_to_hook (f, y, x); + } } /* Similar but don't take any account of the wasted characters. */ @@ -120,9 +124,13 @@ cursor_to (struct frame *f, int vpos, int hpos) void raw_cursor_to (struct frame *f, int row, int col) { - if (FRAME_TERMINAL (f)->raw_cursor_to_hook) - (*FRAME_TERMINAL (f)->raw_cursor_to_hook) (f, row + f->top_pos, - col + f->left_pos); + struct terminal *term = FRAME_TERMINAL (f); + if (term->raw_cursor_to_hook) + { + int x, y; + root_xy (f, row, col, &x, &y); + term->raw_cursor_to_hook (f, y, x); + } } /* Erase operations. */ commit 5e132835ad320be1d5c45ffbf83d67d16fc7bf96 Author: Gerd Möllmann Date: Sat Jan 25 05:33:13 2025 +0100 Simplify absolute (x, y) computation on ttys * src/dispnew.c (root_xy): New function. (frame_pos_abs): Removed. (frame_rect_abs, abs_cursor_pos): Use root_xy. * src/dispextern.h: Declare root_xy. * src/term.c (mouse_get_xy): Use it. diff --git a/src/dispextern.h b/src/dispextern.h index 1060895d0f4..9c193e79fd1 100644 --- a/src/dispextern.h +++ b/src/dispextern.h @@ -3958,7 +3958,7 @@ void combine_updates (Lisp_Object root_frames); void combine_updates_for_frame (struct frame *f, bool inhibit_id_p); void tty_raise_lower_frame (struct frame *f, bool raise); int max_child_z_order (struct frame *parent); -void frame_pos_abs (struct frame *f, int *x, int *y); +void root_xy (struct frame *f, int x, int y, int *rx, int *ry); bool is_frame_ancestor (struct frame *f1, struct frame *f2); INLINE_HEADER_END diff --git a/src/dispnew.c b/src/dispnew.c index 724ec6ece9a..00e59c767e8 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -3313,16 +3313,18 @@ rect_intersect (struct rect *r, struct rect r1, struct rect r2) return true; } -/* Return the absolute position of frame F in *X and *Y. */ +/* Translate (X, Y) relative to frame F to absolute coordinates + in (*X, *Y). */ void -frame_pos_abs (struct frame *f, int *x, int *y) +root_xy (struct frame *f, int x, int y, int *rx, int *ry) { - *x = *y = 0; + *rx = x; + *ry = y; for (; f; f = FRAME_PARENT_FRAME (f)) { - *x += f->left_pos; - *y += f->top_pos; + *rx += f->left_pos; + *ry += f->top_pos; } } @@ -3333,7 +3335,7 @@ static struct rect frame_rect_abs (struct frame *f) { int x, y; - frame_pos_abs (f, &x, &y); + root_xy (f, 0, 0, &x, &y); return (struct rect) { x, y, f->total_cols, f->total_lines }; } @@ -3875,10 +3877,7 @@ abs_cursor_pos (struct frame *f, int *x, int *y) wx += max (0, w->left_margin_cols); - int fx, fy; - frame_pos_abs (f, &fx, &fy); - *x = fx + wx; - *y = fy + wy; + root_xy (f, wx, wy, x, y); return true; } diff --git a/src/term.c b/src/term.c index 4ae9c373888..00bc94e6e31 100644 --- a/src/term.c +++ b/src/term.c @@ -2996,10 +2996,9 @@ mouse_get_xy (int *x, int *y) struct frame *sf = SELECTED_FRAME (); if (f == sf || is_frame_ancestor (sf, f)) { - int fx, fy; - frame_pos_abs (f, &fx, &fy); - *x = fx + XFIXNUM (XCAR (XCDR (mouse))); - *y = fy + XFIXNUM (XCDR (XCDR (mouse))); + int mx = XFIXNUM (XCAR (XCDR (mouse))); + int my = XFIXNUM (XCDR (XCDR (mouse))); + root_xy (f, mx, my, x, y); } } commit a00e6e7143d5ebf7282b5e5b410e1bd882634d57 Author: Stephen Gildea Date: Fri Jan 24 10:12:58 2025 -0800 ; time-stamp: Rename some internal symbols * lisp/time-stamp.el: (time-stamp-string-preprocess): Rename 'alt-form' to 'colon-cnt' because it is now an integer. * test/lisp/time-stamp-tests.el: Rename 'formatz-generate-tests' to 'define-formatz-tests'. diff --git a/lisp/time-stamp.el b/lisp/time-stamp.el index 1ee37b8076b..8d40d7cd194 100644 --- a/lisp/time-stamp.el +++ b/lisp/time-stamp.el @@ -532,7 +532,7 @@ and all `time-stamp-format' compatibility." (let ((prev-char nil) (field-width "") field-result - (alt-form 0) + (colon-cnt 0) (change-case nil) (title-case nil) (upcase nil) @@ -579,7 +579,7 @@ and all `time-stamp-format' compatibility." (setq prev-char cur-char) ;; some characters we actually use (cond ((eq cur-char ?:) - (setq alt-form (1+ alt-form))) + (setq colon-cnt (1+ colon-cnt))) ((eq cur-char ?#) (setq change-case t)) ((eq cur-char ?^) @@ -601,67 +601,67 @@ and all `time-stamp-format' compatibility." ((eq cur-char ?a) ;day of week (time-stamp-do-letter-case nil upcase title-case change-case - (if (> alt-form 0) + (if (> colon-cnt 0) (if (string-equal field-width "") (time-stamp--format "%A" time) "") ;discourage "%:3a" (time-stamp--format "%a" time)))) ((eq cur-char ?A) - (if (and (>= (string-to-number field-width) 1) - (<= (string-to-number field-width) 3) - (not flag-minimize) - (not flag-pad-with-spaces)) - (progn - (time-stamp-conv-warn "%3A" "%#a") - (time-stamp--format "%#a" time)) - (if (or (> alt-form 0) - change-case upcase title-case - flag-minimize flag-pad-with-spaces - (string-equal field-width "")) - (time-stamp-do-letter-case - nil upcase title-case change-case - (time-stamp--format "%A" time)) - (time-stamp-conv-warn (format "%%%sA" field-width) + (cond + ((and (>= (string-to-number field-width) 1) + (<= (string-to-number field-width) 3) + (not flag-minimize) + (not flag-pad-with-spaces)) + (time-stamp-conv-warn "%3A" "%#a") + (time-stamp--format "%#a" time)) + ((or (> colon-cnt 0) + change-case upcase title-case + flag-minimize flag-pad-with-spaces + (string-equal field-width "")) + (time-stamp-do-letter-case + nil upcase title-case change-case + (time-stamp--format "%A" time))) + (t (time-stamp-conv-warn (format "%%%sA" field-width) (format "%%#%sA" field-width) (format "%%:%sA" field-width)) (time-stamp--format "%#A" time)))) ((eq cur-char ?b) ;month name (time-stamp-do-letter-case nil upcase title-case change-case - (if (> alt-form 0) + (if (> colon-cnt 0) (if (string-equal field-width "") (time-stamp--format "%B" time) "") ;discourage "%:3b" (time-stamp--format "%b" time)))) ((eq cur-char ?B) - (if (and (>= (string-to-number field-width) 1) - (<= (string-to-number field-width) 3) - (not flag-minimize) - (not flag-pad-with-spaces)) - (progn - (time-stamp-conv-warn "%3B" "%#b") - (time-stamp--format "%#b" time)) - (if (or (> alt-form 0) - change-case upcase title-case - flag-minimize flag-pad-with-spaces - (string-equal field-width "")) - (time-stamp-do-letter-case - nil upcase title-case change-case - (time-stamp--format "%B" time)) - (time-stamp-conv-warn (format "%%%sB" field-width) + (cond + ((and (>= (string-to-number field-width) 1) + (<= (string-to-number field-width) 3) + (not flag-minimize) + (not flag-pad-with-spaces)) + (time-stamp-conv-warn "%3B" "%#b") + (time-stamp--format "%#b" time)) + ((or (> colon-cnt 0) + change-case upcase title-case + flag-minimize flag-pad-with-spaces + (string-equal field-width "")) + (time-stamp-do-letter-case + nil upcase title-case change-case + (time-stamp--format "%B" time))) + (t (time-stamp-conv-warn (format "%%%sB" field-width) (format "%%#%sB" field-width) (format "%%:%sB" field-width)) (time-stamp--format "%#B" time)))) ((eq cur-char ?d) ;day of month, 1-31 - (time-stamp-do-number cur-char alt-form field-width time)) + (time-stamp-do-number cur-char colon-cnt field-width time)) ((eq cur-char ?H) ;hour, 0-23 - (time-stamp-do-number cur-char alt-form field-width time)) + (time-stamp-do-number cur-char colon-cnt field-width time)) ((eq cur-char ?I) ;hour, 1-12 - (time-stamp-do-number cur-char alt-form field-width time)) + (time-stamp-do-number cur-char colon-cnt field-width time)) ((eq cur-char ?m) ;month number, 1-12 - (time-stamp-do-number cur-char alt-form field-width time)) + (time-stamp-do-number cur-char colon-cnt field-width time)) ((eq cur-char ?M) ;minute, 0-59 - (time-stamp-do-number cur-char alt-form field-width time)) + (time-stamp-do-number cur-char colon-cnt field-width time)) ((eq cur-char ?p) ;AM or PM (time-stamp-do-letter-case t upcase title-case change-case @@ -673,11 +673,11 @@ and all `time-stamp-format' compatibility." t upcase title-case change-case (time-stamp--format "%p" time)))) ((eq cur-char ?S) ;seconds, 00-60 - (time-stamp-do-number cur-char alt-form field-width time)) + (time-stamp-do-number cur-char colon-cnt field-width time)) ((eq cur-char ?w) ;weekday number, Sunday is 0 (time-stamp--format "%w" time)) ((eq cur-char ?y) ;year - (if (= alt-form 0) + (if (= colon-cnt 0) (if (or (string-equal field-width "") (<= (string-to-number field-width) 2)) (string-to-number (time-stamp--format "%y" time)) @@ -706,7 +706,7 @@ and all `time-stamp-format' compatibility." (setq field-width "") (cond (change-case "") ;discourage %z variations - ((and (= alt-form 0) + ((and (= colon-cnt 0) (not flag-minimize) (not flag-pad-with-spaces) (not flag-pad-with-zeros) @@ -717,7 +717,7 @@ and all `time-stamp-format' compatibility." flag-minimize flag-pad-with-spaces flag-pad-with-zeros - alt-form + colon-cnt field-width-num offset-secs))))) ((eq cur-char ?Z) ;time zone name @@ -757,7 +757,7 @@ and all `time-stamp-format' compatibility." (system-name)) )) (and (numberp field-result) - (= alt-form 0) + (= colon-cnt 0) (or (string-equal field-width "") (string-equal field-width "0")) ;; no width provided; set width for default @@ -793,13 +793,13 @@ This is an internal helper for `time-stamp-string-preprocess'." (t text))) -(defun time-stamp-do-number (format-char alt-form field-width time) +(defun time-stamp-do-number (format-char colon-count field-width time) "Handle compatible FORMAT-CHAR where only default width/padding will change. -ALT-FORM is non-0 if \":\" was specified. FIELD-WIDTH is the string +COLON-COUNT is non-0 if \":\" was specified. FIELD-WIDTH is the string width specification or \"\". TIME is the time to convert. This is an internal helper for `time-stamp-string-preprocess'." (let ((format-string (concat "%" (char-to-string format-char)))) - (if (and (> alt-form 0) (not (string-equal field-width ""))) + (if (and (> colon-count 0) (not (string-equal field-width ""))) "" ;discourage "%:2d" and the like (string-to-number (time-stamp--format format-string time))))) diff --git a/test/lisp/time-stamp-tests.el b/test/lisp/time-stamp-tests.el index 6858990b982..397e21f7bc7 100644 --- a/test/lisp/time-stamp-tests.el +++ b/test/lisp/time-stamp-tests.el @@ -69,8 +69,8 @@ (lambda (_old _new &optional _newer) (setq warning-count (1+ warning-count))))) (should ,form) - (if (not (= warning-count 1)) - (ert-fail (format "Should have warned about format: %S" ',form)))))) + (unless (= warning-count 1) + (ert-fail (format "Should have warned about format: %S" ',form)))))) ;;; Tests: @@ -104,16 +104,16 @@ (nth cur-index part-list) (nth 0 part-list)))))) ;; Don't repeat the default pattern. - (if (or (= cur 0) (> cur-index 0)) - ;; The whole format must start with %, so not all - ;; generated combinations are valid - (if (or (not (equal (extract-part 2) "")) + (when (or (= cur 0) (> cur-index 0)) + ;; The whole format must start with %, so not all + ;; generated combinations are valid + (when (or (not (equal (extract-part 2) "")) (equal (extract-part 3) "")) - (iter-yield (list (extract-part 0) - (extract-part 1) - (apply #'concat - (mapcar #'extract-part '(2 3 4))) - (extract-part 5)))))))))) + (iter-yield (list (extract-part 0) + (extract-part 1) + (apply #'concat + (mapcar #'extract-part '(2 3 4))) + (extract-part 5)))))))))) (iter-defun time-stamp-test-pattern-multiply () "Iterate through every combination of parts of `time-stamp-pattern'." @@ -130,9 +130,9 @@ ;; so not all generated combinations are valid. ;; (This is why the format can be supplied as "%%" to ;; preserve the default format.) - (if (or (not (equal format "")) - (equal end "")) - (iter-yield (list line-limit start format end))))))))) + (when (or (not (equal format "")) + (equal end "")) + (iter-yield (list line-limit start format end))))))))) (iter-defun time-stamp-test-pattern-all () (iter-yield-from (time-stamp-test-pattern-sequential)) @@ -1035,7 +1035,7 @@ The functions in `pattern-mod' are composed left to right." ;; Convenience macro for generating groups of test cases. -(defmacro formatz-generate-tests +(defmacro define-formatz-tests (form-strings hour-mod mins-mod secs-mod big-mod secbig-mod) "Define tests for time formats FORM-STRINGS. FORM-STRINGS is a list of formats, each \"%z\" or some variation thereof. @@ -1065,10 +1065,10 @@ the other expected results for hours greater than 99 with non-zero seconds." ;; Generate a form to create a list of tests to define. When this ;; macro is called, the form is evaluated, thus defining the tests. ;; We will modify this list, so start with a list consed at runtime. - (let ((ert-test-list (list 'list)) + (let ((ert-test-list (list 'progn)) (common-description (concat "\nThis test expands from a call to" - " the macro `formatz-generate-tests'.\n" + " the macro `define-formatz-tests'.\n" "To find the specific call, search the source file for \""))) (dolist (form-string form-strings ert-test-list) (nconc @@ -1108,7 +1108,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ;;; Test %z formats without colons. ;; Option character "-" (minus) minimizes; it removes "00" minutes. -(formatz-generate-tests ("%-z" "%-3z") +(define-formatz-tests ("%-z" "%-3z") ("+00") ("+0030" formatz-mod-del-colons) ("+000030" formatz-mod-del-colons) @@ -1116,7 +1116,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ("+100:00:30")) ;; Minus with padding pads with spaces. -(formatz-generate-tests ("%-12z") +(define-formatz-tests ("%-12z") ("+00 " formatz-mod-pad-r12) ("+0030 " formatz-mod-del-colons formatz-mod-pad-r12) ("+000030 " formatz-mod-del-colons formatz-mod-pad-r12) @@ -1124,7 +1124,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ("+100:00:30 " formatz-mod-pad-r12)) ;; 0 after other digits becomes padding of ten, not zero flag. -(formatz-generate-tests ("%-10z") +(define-formatz-tests ("%-10z") ("+00 " formatz-mod-pad-r10) ("+0030 " formatz-mod-del-colons formatz-mod-pad-r10) ("+000030 " formatz-mod-del-colons formatz-mod-pad-r10) @@ -1146,7 +1146,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ;; Basic %z outputs 4 digits. ;; Small padding values do not extend the result. -(formatz-generate-tests (;; We don't check %z here because time-stamp +(define-formatz-tests (;; We don't check %z here because time-stamp ;; has a legacy behavior for it. ;;"%z" "%5z" "%0z" "%05z") @@ -1157,7 +1157,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ("+100:00:30")) ;; Padding adds spaces. -(formatz-generate-tests ("%12z") +(define-formatz-tests ("%12z") ("+0000 " formatz-mod-add-00 formatz-mod-pad-r12) ("+0030 " formatz-mod-del-colons formatz-mod-pad-r12) ("+000030 " formatz-mod-del-colons formatz-mod-pad-r12) @@ -1165,7 +1165,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ("+100:00:30 " formatz-mod-pad-r12)) ;; Requiring 0-padding to 6 adds seconds (only) as needed. -(formatz-generate-tests ("%06z") +(define-formatz-tests ("%06z") ("+000000" formatz-mod-add-00 formatz-mod-add-00) ("+003000" formatz-mod-del-colons formatz-mod-add-00) ("+000030" formatz-mod-del-colons) @@ -1173,7 +1173,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ("+100:00:30")) ;; Option character "_" always adds seconds. -(formatz-generate-tests ("%_z" "%_7z") +(define-formatz-tests ("%_z" "%_7z") ("+000000" formatz-mod-add-00 formatz-mod-add-00) ("+003000" formatz-mod-del-colons formatz-mod-add-00) ("+000030" formatz-mod-del-colons) @@ -1181,7 +1181,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ("+100:00:30")) ;; Enough 0-padding adds seconds, then adds spaces. -(formatz-generate-tests ("%012z" "%_12z") +(define-formatz-tests ("%012z" "%_12z") ("+000000 " formatz-mod-add-00 formatz-mod-add-00 formatz-mod-pad-r12) ("+003000 " formatz-mod-del-colons formatz-mod-add-00 formatz-mod-pad-r12) ("+000030 " formatz-mod-del-colons formatz-mod-pad-r12) @@ -1192,7 +1192,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ;; Three colons can output hours only, ;; like %-z, but uses colons with non-zero minutes and seconds. -(formatz-generate-tests ("%:::z" "%0:::z" +(define-formatz-tests ("%:::z" "%0:::z" "%3:::z" "%03:::z") ("+00") ("+00:30") @@ -1201,7 +1201,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ("+100:00:30")) ;; Padding with three colons adds spaces. -(formatz-generate-tests ("%12:::z") +(define-formatz-tests ("%12:::z") ("+00 " formatz-mod-pad-r12) ("+00:30 " formatz-mod-pad-r12) ("+00:00:30 " formatz-mod-pad-r12) @@ -1209,7 +1209,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ("+100:00:30 " formatz-mod-pad-r12)) ;; 0 after other digits becomes padding of ten, not zero flag. -(formatz-generate-tests ("%10:::z") +(define-formatz-tests ("%10:::z") ("+00 " formatz-mod-pad-r10) ("+00:30 " formatz-mod-pad-r10) ("+00:00:30 " formatz-mod-pad-r10) @@ -1217,7 +1217,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ("+100:00:30")) ;; One colon outputs minutes, like %z but with colon. -(formatz-generate-tests ("%:z" "%6:z" "%0:z" "%06:z" "%06:::z") +(define-formatz-tests ("%:z" "%6:z" "%0:z" "%06:z" "%06:::z") ("+00:00" formatz-mod-add-colon00) ("+00:30") ("+00:00:30") @@ -1225,7 +1225,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ("+100:00:30")) ;; Padding with one colon adds spaces. -(formatz-generate-tests ("%12:z") +(define-formatz-tests ("%12:z") ("+00:00 " formatz-mod-add-colon00 formatz-mod-pad-r12) ("+00:30 " formatz-mod-pad-r12) ("+00:00:30 " formatz-mod-pad-r12) @@ -1233,7 +1233,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ("+100:00:30 " formatz-mod-pad-r12)) ;; Requiring 0-padding to 7 adds seconds (only) as needed. -(formatz-generate-tests ("%07:z" "%07:::z") +(define-formatz-tests ("%07:z" "%07:::z") ("+00:00:00" formatz-mod-add-colon00 formatz-mod-add-colon00) ("+00:30:00" formatz-mod-add-colon00) ("+00:00:30") @@ -1241,7 +1241,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ("+100:00:30")) ;; Two colons outputs HH:MM:SS, like %_z but with colons. -(formatz-generate-tests ("%::z" "%9::z" "%0::z" "%09::z") +(define-formatz-tests ("%::z" "%9::z" "%0::z" "%09::z") ("+00:00:00" formatz-mod-add-colon00 formatz-mod-add-colon00) ("+00:30:00" formatz-mod-add-colon00) ("+00:00:30") @@ -1249,7 +1249,7 @@ the other expected results for hours greater than 99 with non-zero seconds." ("+100:00:30")) ;; Enough padding adds minutes and seconds, then adds spaces. -(formatz-generate-tests ("%012:z" "%012::z" "%12::z" "%012:::z") +(define-formatz-tests ("%012:z" "%012::z" "%12::z" "%012:::z") ("+00:00:00 " formatz-mod-add-colon00 formatz-mod-add-colon00 formatz-mod-pad-r12) ("+00:30:00 " formatz-mod-add-colon00 formatz-mod-pad-r12) commit 77fe4fbeffc723337bd295e593c8062cd4ab7e43 Author: Stephen Gildea Date: Fri Jan 24 09:44:03 2025 -0800 ; * lisp/time-stamp.el: Improve two documentation strings diff --git a/lisp/time-stamp.el b/lisp/time-stamp.el index 2aa5ba74dc3..1ee37b8076b 100644 --- a/lisp/time-stamp.el +++ b/lisp/time-stamp.el @@ -488,10 +488,15 @@ Internal helper used by `time-stamp-string-preprocess'." (format-time-string format time time-stamp-time-zone)) (defun time-stamp-string (&optional ts-format time) - "Generate the new string to be inserted by \\[time-stamp]. -Optionally use format TS-FORMAT instead of `time-stamp-format' to -format the string. Optional second argument TIME is only for testing; -normally the current time is used." + "Return the current time and other info formatted for \\[time-stamp]. +Optional first argument TS-FORMAT gives the format to use; it defaults +to the value of `time-stamp-format'. Thus, with no arguments, +this function returns the string `time-stamp' would use to update +its template in the buffer. The format accepted is similar to the +format used by `format-time-string' with some extensions; see the +documentation of `time-stamp-format' for details. +Optional second argument TIME is only for testing; normally the current +time is used. The time zone is determined by `time-stamp-time-zone'." (if (stringp (or ts-format (setq ts-format time-stamp-format))) (time-stamp-string-preprocess ts-format time))) @@ -790,7 +795,7 @@ This is an internal helper for `time-stamp-string-preprocess'." (defun time-stamp-do-number (format-char alt-form field-width time) "Handle compatible FORMAT-CHAR where only default width/padding will change. -ALT-FORM is whether `#' was specified. FIELD-WIDTH is the string +ALT-FORM is non-0 if \":\" was specified. FIELD-WIDTH is the string width specification or \"\". TIME is the time to convert. This is an internal helper for `time-stamp-string-preprocess'." (let ((format-string (concat "%" (char-to-string format-char)))) commit 0d38f1d07b27ad3634a667388c39d55ed2bb6137 Author: Stephen Gildea Date: Fri Jan 24 08:54:13 2025 -0800 ; * doc/emacs/files.texi: only one time stamp index entry diff --git a/doc/emacs/files.texi b/doc/emacs/files.texi index 1d3fc59e3fe..87c4638be04 100644 --- a/doc/emacs/files.texi +++ b/doc/emacs/files.texi @@ -995,8 +995,7 @@ File Shadowing is not available on MS Windows. @node Time Stamps @subsection Updating Time Stamps Automatically -@cindex time stamps -@cindex timestamps +@cindex time stamps, automatic file timestamps @cindex modification dates @cindex last modified time commit 07a2a67e3bfd674daad7a7d8947a0ab67b4e13e8 Author: Gerd Möllmann Date: Fri Jan 24 11:18:54 2025 +0100 Disable more redisplay optimizations when child frames are visible * src/xdisp.c (redisplay_internal): Disable more optimizations on a tty root frame displaying a child frame. (try_cursor_movement,(try_window_reusing_current_matrix) (try_window_id): Don't use on tty root frames displaying a child frame. diff --git a/src/xdisp.c b/src/xdisp.c index ba8ba1b72e3..0497490928f 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -17214,7 +17214,11 @@ redisplay_internal (void) /* All text outside that line, including its final newline, must be unchanged. */ && text_outside_line_unchanged_p (w, CHARPOS (tlbufpos), - CHARPOS (tlendpos))) + CHARPOS (tlendpos)) + /* If this is a window on a tty root frame displaying a child frame, + the current matrix of W may contain glyphs of that child frame. + Don't try shortcuts that might use the current matrix in this case. */ + && !is_tty_root_frame_with_visible_child (XFRAME (w->frame))) { if (CHARPOS (tlbufpos) > BEGV && FETCH_BYTE (BYTEPOS (tlbufpos) - 1) != '\n' @@ -17279,11 +17283,7 @@ redisplay_internal (void) line and this line is the current one, because display_line above is not informed about the current-line's vpos, and cannot DTRT in that case. */ - && !hscrolling_current_line_p (w) - /* A root frame may have visible children displayed in its - current matrix, so that we can't do the below with its - current matrix. */ - && !is_tty_root_frame_with_visible_child (it.f)) + && !hscrolling_current_line_p (w)) { /* If this is not the window's last line, we must adjust the charstarts of the lines below. */ @@ -19430,6 +19430,13 @@ try_cursor_movement (Lisp_Object window, struct text_pos startp, struct frame *f = XFRAME (w->frame); int rc = CURSOR_MOVEMENT_CANNOT_BE_USED; + + /* If this is a window on a tty root frame displaying a child frame, + the current matrix of W may contain glyphs of that child frame, + so this method is not safe to use. */ + if (is_tty_root_frame_with_visible_child (f)) + return rc; + #ifdef GLYPH_DEBUG if (inhibit_try_cursor_movement) return rc; @@ -21333,6 +21340,13 @@ static bool try_window_reusing_current_matrix (struct window *w) { struct frame *f = XFRAME (w->frame); + + /* If this is a window on a tty root frame displaying a child frame, + the current matrix of W may contain glyphs of that child frame, + so this method is not safe to use. */ + if (is_tty_root_frame_with_visible_child (f)) + return false; + struct glyph_row *bottom_row; struct it it; struct run run; @@ -22120,6 +22134,13 @@ static int try_window_id (struct window *w) { struct frame *f = XFRAME (w->frame); + + /* If this is a window on a tty root frame displaying a child frame, + the current matrix of W may contain glyphs of that child frame, + so this method is not safe to use. */ + if (is_tty_root_frame_with_visible_child (f)) + return 0; + struct glyph_matrix *current_matrix = w->current_matrix; struct glyph_matrix *desired_matrix = w->desired_matrix; struct glyph_row *last_unchanged_at_beg_row; commit 5e657ad1fe2567f7d21639dbac40949870fe76f9 Author: Eli Zaretskii Date: Fri Jan 24 10:41:00 2025 +0200 ; * src/dispnew.c (combine_updates_for_frame): Fix whitespace. diff --git a/src/dispnew.c b/src/dispnew.c index e4567f6970c..724ec6ece9a 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -3967,7 +3967,7 @@ combine_updates_for_frame (struct frame *f, bool inhibit_scrolling) /* Determine visible frames on the root frame, including the root frame itself. Note that there are cases, see bug#75056, where we - can be called for invisible frames. */ + can be called for invisible frames. */ Lisp_Object z_order = frames_in_reverse_z_order (root, true); if (NILP (z_order)) { commit 6c633ece6e6743d9724ff116322e573ddf79d79f Author: Eli Zaretskii Date: Fri Jan 24 10:40:14 2025 +0200 Revert "; * src/dispnew.c (combine_updates_for_frame): Fix whitespace." This reverts commit c941b94e51f4e5996718416ac908249cb71da1d9. It was pushed by mistake. diff --git a/src/dispnew.c b/src/dispnew.c index 724ec6ece9a..e4567f6970c 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -3967,7 +3967,7 @@ combine_updates_for_frame (struct frame *f, bool inhibit_scrolling) /* Determine visible frames on the root frame, including the root frame itself. Note that there are cases, see bug#75056, where we - can be called for invisible frames. */ + can be called for invisible frames. */ Lisp_Object z_order = frames_in_reverse_z_order (root, true); if (NILP (z_order)) { diff --git a/test/src/editfns-tests.el b/test/src/editfns-tests.el index 6da3c73f001..9fff4255b57 100644 --- a/test/src/editfns-tests.el +++ b/test/src/editfns-tests.el @@ -534,14 +534,4 @@ 'utf-8 nil (current-buffer)) (should (null (sanity-check-change-functions-errors)))))) -(ert-deftest editfns-tests-styled-print () - (let* ((print-unreadable-function - (lambda (&rest args) - (garbage-collect) - (make-string 100 ?Ā t))) - (str "\"[1] ĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀ\"")) - (should (string= - (format "%S" (format "%S %S" [1] (symbol-function '+))) str)))) - - ;;; editfns-tests.el ends here commit c941b94e51f4e5996718416ac908249cb71da1d9 Author: Eli Zaretskii Date: Fri Jan 24 10:26:26 2025 +0200 ; * src/dispnew.c (combine_updates_for_frame): Fix whitespace. diff --git a/src/dispnew.c b/src/dispnew.c index e4567f6970c..724ec6ece9a 100644 --- a/src/dispnew.c +++ b/src/dispnew.c @@ -3967,7 +3967,7 @@ combine_updates_for_frame (struct frame *f, bool inhibit_scrolling) /* Determine visible frames on the root frame, including the root frame itself. Note that there are cases, see bug#75056, where we - can be called for invisible frames. */ + can be called for invisible frames. */ Lisp_Object z_order = frames_in_reverse_z_order (root, true); if (NILP (z_order)) { diff --git a/test/src/editfns-tests.el b/test/src/editfns-tests.el index 9fff4255b57..6da3c73f001 100644 --- a/test/src/editfns-tests.el +++ b/test/src/editfns-tests.el @@ -534,4 +534,14 @@ 'utf-8 nil (current-buffer)) (should (null (sanity-check-change-functions-errors)))))) +(ert-deftest editfns-tests-styled-print () + (let* ((print-unreadable-function + (lambda (&rest args) + (garbage-collect) + (make-string 100 ?Ā t))) + (str "\"[1] ĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀĀ\"")) + (should (string= + (format "%S" (format "%S %S" [1] (symbol-function '+))) str)))) + + ;;; editfns-tests.el ends here