27 files changed, 2126 insertions(+), 687 deletions(-) .gitignore | 1 - lisp/disp-table.el | 54 ++- lisp/frame.el | 46 +- lisp/paren.el | 4 +- lisp/tty-tip.el | 208 +++++++++ lisp/xt-mouse.el | 24 +- src/alloc.c | 8 +- src/chartab.c | 2 - src/dispextern.h | 40 +- src/dispnew.c | 1321 ++++++++++++++++++++++++++++++++++++++-------------- src/disptab.h | 12 +- src/frame.c | 508 +++++++++++++++----- src/frame.h | 83 ++-- src/keyboard.c | 2 +- src/minibuf.c | 6 +- src/nsfns.m | 4 +- src/nsterm.m | 4 +- src/scroll.c | 4 +- src/term.c | 303 ++++++++++-- src/termhooks.h | 3 + src/terminal.c | 6 +- src/treesit.c | 2 +- src/w32console.c | 15 +- src/w32inevt.c | 11 +- src/w32term.c | 25 +- src/xdisp.c | 116 +++-- src/xfaces.c | 1 - modified .gitignore @@ -323,7 +323,6 @@ gnustmp* *~ \#*\# ChangeLog -[0-9]*.patch [0-9]*.txt /vc-dwim-log-* modified lisp/disp-table.el @@ -28,7 +28,7 @@ ;;; Code: -(put 'display-table 'char-table-extra-slots 6) +(put 'display-table 'char-table-extra-slots 12) ;;;###autoload (defun make-display-table () @@ -46,13 +46,21 @@ make-display-table (put 'control 'display-table-slot 3) (put 'selective-display 'display-table-slot 4) (put 'vertical-border 'display-table-slot 5) +(put 'box-vertical 'display-table-slot 6) +(put 'box-horizontal 'display-table-slot 7) +(put 'box-down-right 'display-table-slot 8) +(put 'box-down-left 'display-table-slot 9) +(put 'box-up-right 'display-table-slot 10) +(put 'box-up-left 'display-table-slot 11) ;;;###autoload (defun display-table-slot (display-table slot) "Return the value of the extra slot in DISPLAY-TABLE named SLOT. -SLOT may be a number from 0 to 5 inclusive, or a slot name (symbol). +SLOT may be a number from 0 to 11 inclusive, or a slot name (symbol). Valid symbols are `truncation', `wrap', `escape', `control', -`selective-display', and `vertical-border'." +`selective-display', `vertical-border', `box-vertical', +`box-horizontal', `box-down-right', `box-down-left', `box-up-right', +and `box-up-left'." (let ((slot-number (if (numberp slot) slot (or (get slot 'display-table-slot) @@ -62,9 +70,11 @@ display-table-slot ;;;###autoload (defun set-display-table-slot (display-table slot value) "Set the value of the extra slot in DISPLAY-TABLE named SLOT to VALUE. -SLOT may be a number from 0 to 5 inclusive, or a name (symbol). +SLOT may be a number from 0 to 11 inclusive, or a name (symbol). Valid symbols are `truncation', `wrap', `escape', `control', -`selective-display', and `vertical-border'." +`selective-display', `vertical-border', `box-vertical', +`box-horizontal', `box-down-right', `box-down-left', `box-up-right', +and `box-up-left'." (let ((slot-number (if (numberp slot) slot (or (get slot 'display-table-slot) @@ -87,6 +97,18 @@ describe-display-table (prin1 (display-table-slot dt 'selective-display)) (princ "\nVertical window border glyph: ") (prin1 (display-table-slot dt 'vertical-border)) + (princ "\nBox vertical line glyph: ") + (prin1 (display-table-slot dt 'box-vertical)) + (princ "\nBox horizonal line glyph: ") + (prin1 (display-table-slot dt 'box-horizontal)) + (princ "\nBox upper left corner glyph: ") + (prin1 (display-table-slot dt 'box-down-right)) + (princ "\nBox upper right corner glyph: ") + (prin1 (display-table-slot dt 'box-down-left)) + (princ "\nBox lower left corner glyph: ") + (prin1 (display-table-slot dt 'box-up-right)) + (princ "\nBox lower right corner glyph: ") + (prin1 (display-table-slot dt 'box-up-left)) (princ "\nCharacter display glyph sequences:\n") (with-current-buffer standard-output (let ((vector (make-vector 256 nil)) @@ -126,6 +148,28 @@ describe-current-display-table (describe-display-table disptab) (message "No display table")))) +;;;###autoload +(defun standard-display-unicode-special-glyphs () + "Display some glyps using Unicode characters. +The glyphs being changed by this function are `vertical-border', +`box-vertical', `box-horizontal', `box-down-right', `box-down-left', +`box-up-right', and `box-up-left'." + (interactive) + (set-display-table-slot standard-display-table + 'vertical-border (make-glyph-code #x2502)) + (set-display-table-slot standard-display-table + 'box-vertical (make-glyph-code #x2502)) + (set-display-table-slot standard-display-table + 'box-horizontal (make-glyph-code #x2500)) + (set-display-table-slot standard-display-table + 'box-down-right (make-glyph-code #x250c)) + (set-display-table-slot standard-display-table + 'box-down-left (make-glyph-code #x2510)) + (set-display-table-slot standard-display-table + 'box-up-right (make-glyph-code #x2514)) + (set-display-table-slot standard-display-table + 'box-up-left (make-glyph-code #x2518))) + ;;;###autoload (defun standard-display-8bit (l h) "Display characters representing raw bytes in the range L to H literally. modified lisp/frame.el @@ -1495,6 +1495,13 @@ frame-outer-height (let ((edges (frame-edges frame 'outer-edges))) (- (nth 3 edges) (nth 1 edges)))) +(defun frame-at (x y) + "Return frame containing pixel position X, Y." + (cl-loop for frame in (frame-list-z-order) + as (x0 y0 x1 y1) = (frame-edges frame) + when (and (<= x0 x (1- x1)) (<= y0 y (1- y1))) + return frame)) + (declare-function x-list-fonts "xfaces.c" (pattern &optional face frame maximum width)) @@ -1722,6 +1729,7 @@ frame-current-scroll-bars (declare-function pgtk-frame-geometry "pgtkfns.c" (&optional frame)) (declare-function haiku-frame-geometry "haikufns.c" (&optional frame)) (declare-function android-frame-geometry "androidfns.c" (&optional frame)) +(declare-function tty-frame-geometry "term.c" (&optional frame)) (defun frame-geometry (&optional frame) "Return geometric attributes of FRAME. @@ -1778,24 +1786,7 @@ frame-geometry ((eq frame-type 'android) (android-frame-geometry frame)) (t - (list - '(outer-position 0 . 0) - (cons 'outer-size (cons (frame-width frame) (frame-height frame))) - '(external-border-size 0 . 0) - '(outer-border-width . 0) - '(title-bar-size 0 . 0) - '(menu-bar-external . nil) - (let ((menu-bar-lines (frame-parameter frame 'menu-bar-lines))) - (cons 'menu-bar-size - (if menu-bar-lines - (cons (frame-width frame) 1) - 1 0))) - '(tool-bar-external . nil) - '(tool-bar-position . nil) - '(tool-bar-size 0 . 0) - '(tab-bar-size 0 . 0) - (cons 'internal-border-width - (frame-parameter frame 'internal-border-width))))))) + (tty-frame-geometry frame))))) (defun frame--size-history (&optional frame) "Print history of resize operations for FRAME. @@ -1904,6 +1895,7 @@ frame--size-history (declare-function pgtk-frame-edges "pgtkfns.c" (&optional frame type)) (declare-function haiku-frame-edges "haikufns.c" (&optional frame type)) (declare-function android-frame-edges "androidfns.c" (&optional frame type)) +(declare-function tty-frame-edges "term.c" (&optional frame type)) (defun frame-edges (&optional frame type) "Return coordinates of FRAME's edges. @@ -1934,7 +1926,7 @@ frame-edges ((eq frame-type 'android) (android-frame-edges frame type)) (t - (list 0 0 (frame-width frame) (frame-height frame)))))) + (tty-frame-edges frame type))))) (declare-function w32-mouse-absolute-pixel-position "w32fns.c") (declare-function x-mouse-absolute-pixel-position "xfns.c") @@ -2087,6 +2079,7 @@ frame-monitor-workarea ;; (declare-function pgtk-frame-list-z-order "pgtkfns.c" (&optional display)) (declare-function haiku-frame-list-z-order "haikufns.c" (&optional display)) (declare-function android-frame-list-z-order "androidfns.c" (&optional display)) +(declare-function tty-frame-list-z-order "term.c" (&optional display)) (defun frame-list-z-order (&optional display) "Return list of Emacs's frames, in Z (stacking) order. @@ -2114,7 +2107,9 @@ frame-list-z-order ((eq frame-type 'haiku) (haiku-frame-list-z-order display)) ((eq frame-type 'android) - (android-frame-list-z-order display))))) + (android-frame-list-z-order display)) + (t + (tty-frame-list-z-order display))))) (declare-function x-frame-restack "xfns.c" (frame1 frame2 &optional above)) (declare-function w32-frame-restack "w32fns.c" (frame1 frame2 &optional above)) @@ -2123,6 +2118,7 @@ frame-list-z-order (declare-function haiku-frame-restack "haikufns.c" (frame1 frame2 &optional above)) (declare-function android-frame-restack "androidfns.c" (frame1 frame2 &optional above)) +(declare-function tty-frame-restack "term.c" (frame1 frame2 &optional above)) (defun frame-restack (frame1 frame2 &optional above) "Restack FRAME1 below FRAME2. @@ -2158,7 +2154,9 @@ frame-restack ((eq frame-type 'pgtk) (pgtk-frame-restack frame1 frame2 above)) ((eq frame-type 'android) - (android-frame-restack frame1 frame2 above)))) + (android-frame-restack frame1 frame2 above)) + (t + (tty-frame-restack frame1 frame2 above)))) (error "Cannot restack frames"))) (defun frame-size-changed-p (&optional frame) @@ -2311,6 +2309,7 @@ display-screens 1)))) (declare-function x-display-pixel-height "xfns.c" (&optional terminal)) +(declare-function tty-display-pixel-height "term.c" (&optional terminal)) (defun display-pixel-height (&optional display) "Return the height of DISPLAY's screen in pixels. @@ -2328,9 +2327,10 @@ display-pixel-height ((memq frame-type '(x w32 ns haiku pgtk android)) (x-display-pixel-height display)) (t - (frame-height (if (framep display) display (selected-frame))))))) + (tty-display-pixel-height display))))) (declare-function x-display-pixel-width "xfns.c" (&optional terminal)) +(declare-function tty-display-pixel-width "term.c" (&optional terminal)) (defun display-pixel-width (&optional display) "Return the width of DISPLAY's screen in pixels. @@ -2348,7 +2348,7 @@ display-pixel-width ((memq frame-type '(x w32 ns haiku pgtk android)) (x-display-pixel-width display)) (t - (frame-width (if (framep display) display (selected-frame))))))) + (tty-display-pixel-width display))))) (defcustom display-mm-dimensions-alist nil "Alist for specifying screen dimensions in millimeters. modified lisp/paren.el @@ -522,9 +522,7 @@ show-paren-function openparen)) (message-log-max nil)) (cond - ((and - (eq show-paren-context-when-offscreen 'child-frame) - (display-graphic-p)) + ((eq show-paren-context-when-offscreen 'child-frame) (show-paren--show-context-in-child-frame context)) ((eq show-paren-context-when-offscreen 'overlay) (show-paren--show-context-in-overlay context)) new file lisp/tty-tip.el @@ -0,0 +1,208 @@ +;;; -*- lexical-binding: t; symbol-packages: t; -*- +;;; tty-tip.el --- Display help in kind of tooltips on ttys + +;; Copyright (C) 2024 Free Software Foundation, Inc. + +;; This file is part of GNU Emacs. + +;; GNU Emacs is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; GNU Emacs is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with GNU Emacs. If not, see . + +;;; Commentary: + +;; This uses tty child frames to display help which looks and feels much +;; like using tooltips (but they really aren't). + +;; Use `tty-tip-mode' to activate or toggle this feature. +;; +;; You can customize face `tooltip', `tooltip-short-delay', +;; `tooltip-delay', `tooltip-recent-seconds'. + +(require 'tooltip) + +(defvar tty-tip--frame nil) + +(defun tty-tip--make-buffer (text) + (with-current-buffer + (get-buffer-create " *tty-tip*") + ;; Redirect focus to parent. + (add-hook 'pre-command-hook #'tty-tip--delete-frame nil t) + ;; Use an empty keymap. + (use-local-map (make-keymap)) + (dolist (var '((mode-line-format . nil) + (header-line-format . nil) + (tab-line-format . nil) + (tab-bar-format . nil) ;; Emacs 28 tab-bar-format + (frame-title-format . "") + (truncate-lines . t) + (cursor-in-non-selected-windows . nil) + (cursor-type . nil) + (show-trailing-whitespace . nil) + (display-line-numbers . nil) + (left-fringe-width . nil) + (right-fringe-width . nil) + (left-margin-width . 0) + (right-margin-width . 0) + (fringes-outside-margins . 0) + (buffer-read-only . t))) + (set (make-local-variable (car var)) (cdr var))) + (let ((inhibit-modification-hooks t) + (inhibit-read-only t)) + (erase-buffer) + (insert text) + (goto-char (point-min))) + (current-buffer))) + +(defvar tty-tip-frame-parameters + `((visibility . nil) + (background-color . "lightyellow") + (foreground-color . "black") + (width . 0) (height . 0) + (min-width . t) (min-height . t) + (no-accept-focus . t) + (no-focus-on-map . t) + (border-width . 0) + (child-frame-border-width . 1) + (left-fringe . 0) + (right-fringe . 0) + (vertical-scroll-bars . nil) + (horizontal-scroll-bars . nil) + (menu-bar-lines . 0) + (tool-bar-lines . 0) + (tab-bar-lines . 0) + (no-other-frame . t) + (no-other-window . t) + (no-delete-other-windows . t) + (unsplittable . t) + (undecorated . t) + (cursor-type . nil) + (no-special-glyphs . t) + (desktop-dont-save . t))) + +(defun tty-tip--frame-parameters () + (let ((params (copy-sequence tty-tip-frame-parameters)) + (fg (face-attribute 'tooltip :foreground)) + (bg (face-attribute 'tooltip :background))) + (when (stringp fg) + (setf (alist-get 'foreground-color params) fg)) + (when (stringp bg) + (setf (alist-get 'background-color params) bg)) + params)) + +(defvar tty-tip--help-message nil) +(defvar tty-tip--hide-time nil) +(defvar tty-tip--show-timer nil) +(defvar tty-tip--hide-timer nil) + +(defun tty-tip--delete-frame () + (when tty-tip--frame + (when tty-tip--hide-timer + (cancel-timer tty-tip--hide-timer) + (setq tty-tip--hide-timer nil)) + (delete-frame tty-tip--frame) + (setq tty-tip--frame nil) + t)) + +(defun tty-tip--compute-position () + (let* ((pos (mouse-position)) + (mouse-x (car (cdr pos))) + (mouse-y (cdr (cdr pos))) + (x (+ mouse-x 1)) + (y (+ mouse-y 1)) + (tip-width (frame-width tty-tip--frame)) + (tip-height (frame-height tty-tip--frame)) + (tty-width (display-pixel-width)) + (tty-height (display-pixel-height))) + (when (> (+ x tip-width) tty-width) + (setq x (max 0 (- x tip-width 1)))) + (when (> (+ y tip-height) tty-height) + (setq y (max 0 (- y tip-height 1)))) + (cons x y))) + +(defun tty-tip--create-frame (text) + (let* ((minibuffer (minibuffer-window (window-frame))) + (buffer (tty-tip--make-buffer text)) + (window-min-height 1) + (window-min-width 1) + after-make-frame-functions + (text-lines (string-lines text))) + (setq tty-tip--frame + (make-frame + `((parent-frame . ,(car (mouse-position))) + (minibuffer . ,minibuffer) + ,@(tty-tip--frame-parameters)))) + (let ((win (frame-root-window tty-tip--frame))) + (set-window-buffer win buffer) + (set-window-dedicated-p win t) + (set-frame-size tty-tip--frame + (apply #'max (mapcar #'string-width text-lines)) + (length text-lines)) + (let* ((pos (tty-tip--compute-position)) + (x (car pos)) + (y (cdr pos))) + (set-frame-position tty-tip--frame x y)) + (make-frame-visible tty-tip--frame) + (setq tty-tip--hide-timer + (run-with-timer tooltip-hide-delay nil + #'tty-tip--delete-frame))))) + +(defun tty-tip--delay () + (if (and tty-tip--hide-time + (time-less-p (time-since tty-tip--hide-time) + tooltip-recent-seconds)) + tooltip-short-delay + tooltip-delay)) + +(defun tty-tip--cancel-delayed-tip () + (when tty-tip--show-timer + (cancel-timer tty-tip--show-timer) + (setq tty-tip--show-timer nil))) + +(defun tty-tip--start-delayed-tip () + (setq tty-tip--show-timer + (run-with-timer (tty-tip--delay) nil + (lambda () + (tty-tip--create-frame + tty-tip--help-message))))) + +(defun tty-tip--hide (&optional _ignored-arg) + (tty-tip--cancel-delayed-tip) + (when (tty-tip--delete-frame) + (setq tty-tip--hide-time (float-time)))) + +(defun tty-tip--show-help (msg) + (let ((previous-help tty-tip--help-message)) + (setq tty-tip--help-message msg) + (cond ((null msg) + (tty-tip--hide)) + ((equal previous-help msg) + nil) + (t + (tty-tip--hide) + (tty-tip--start-delayed-tip))))) + +;;;###autoload +(define-minor-mode tty-tip-mode + "Global minor mode for displaying help in tty child frames." + :global t :group 'help + (unless (display-graphic-p) + (cond (tty-tip-mode + (setq show-help-function #'tty-tip--show-help) + (add-hook 'pre-command-hook #'tty-tip--hide)) + (t + (setq show-help-function nil) + (remove-hook 'pre-command-hook #'tty-tip--hide))))) + +(provide 'tty-tip) + +;;; End modified lisp/xt-mouse.el @@ -133,7 +133,8 @@ xterm-mouse-translate-1 (defun xterm-mouse--handle-mouse-movement () "Handle mouse motion that was just generated for XTerm mouse." - (display--update-for-mouse-movement (terminal-parameter nil 'xterm-mouse-x) + (display--update-for-mouse-movement (terminal-parameter nil 'xterm-mouse-frame) + (terminal-parameter nil 'xterm-mouse-x) (terminal-parameter nil 'xterm-mouse-y))) ;; These two variables have been converted to terminal parameters. @@ -150,10 +151,11 @@ xt-mouse-epoch (defun xterm-mouse-position-function (pos) "Bound to `mouse-position-function' in XTerm mouse mode." - (when (terminal-parameter nil 'xterm-mouse-x) - (setcdr pos (cons (terminal-parameter nil 'xterm-mouse-x) - (terminal-parameter nil 'xterm-mouse-y)))) - pos) + (if (terminal-parameter nil 'xterm-mouse-x) + (cons (terminal-parameter nil 'xterm-mouse-frame) + (cons (terminal-parameter nil 'xterm-mouse-x) + (terminal-parameter nil 'xterm-mouse-y))) + pos)) (define-obsolete-function-alias 'xterm-mouse-truncate-wrap 'truncate "27.1") @@ -293,7 +295,16 @@ xterm-mouse-event (progn (setq xt-mouse-epoch (float-time)) 0) (car (time-convert (time-since xt-mouse-epoch) 1000)))) - (w (window-at x y)) + (frame (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))) + (w (window-at x y frame)) + ;;(_ (message (format "*** %S" w))) + (ltrb (window-edges w)) (left (nth 0 ltrb)) (top (nth 1 ltrb)) @@ -345,6 +356,7 @@ xterm-mouse-event (set-terminal-parameter nil 'xterm-mouse-x x) (set-terminal-parameter nil 'xterm-mouse-y y) + (set-terminal-parameter nil 'xterm-mouse-frame frame) (setq last-input-event event))))) ;;;###autoload modified src/alloc.c @@ -6853,9 +6853,11 @@ mark_glyph_matrix (struct glyph_matrix *matrix) struct glyph *end_glyph = glyph + row->used[area]; for (; glyph < end_glyph; ++glyph) - if (STRINGP (glyph->object) - && !string_marked_p (XSTRING (glyph->object))) - mark_object (glyph->object); + { + if (STRINGP (glyph->object) + && !string_marked_p (XSTRING (glyph->object))) + mark_object (glyph->object); + } } } } modified src/chartab.c @@ -122,8 +122,6 @@ DEFUN ("make-char-table", Fmake_char_table, Smake_char_table, 1, 2, 0, else { CHECK_FIXNAT (n); - if (XFIXNUM (n) > 10) - args_out_of_range (n, Qnil); n_extras = XFIXNUM (n); } modified src/dispextern.h @@ -482,6 +482,11 @@ #define SET_GLYPH_FROM_GLYPH_CODE(glyph, gc) \ continuation glyphs, or the overlay-arrow glyphs on TTYs. */ Lisp_Object object; + /* Frame on which the glyph was produced. The face_id of this glyph + refers to the face_cache of this frame. This is used on tty + frames only. */ + struct frame *frame; + /* Width in pixels. */ short pixel_width; @@ -626,10 +631,12 @@ #define FACE_ID_BITS 20 #define FONT_TYPE_UNKNOWN 0 -/* Is GLYPH a space? */ +/* Is GLYPH a space in default face on frame FRAME? */ -#define CHAR_GLYPH_SPACE_P(GLYPH) \ - ((GLYPH).u.ch == SPACEGLYPH && (GLYPH).face_id == DEFAULT_FACE_ID) +# define CHAR_GLYPH_SPACE_P(FRAME, GLYPH) \ + ((GLYPH).u.ch == SPACEGLYPH \ + && (GLYPH).face_id == DEFAULT_FACE_ID \ + && (GLYPH).frame == (FRAME)) /* Are glyph slices of glyphs *X and *Y equal? It assumes that both glyphs have the same type. @@ -654,6 +661,7 @@ #define GLYPH_EQUAL_P(X, Y) \ && (X)->u.val == (Y)->u.val \ && GLYPH_SLICE_EQUAL_P (X, Y) \ && (X)->face_id == (Y)->face_id \ + && (X)->frame == (Y)->frame \ && (X)->padding_p == (Y)->padding_p \ && (X)->left_box_line_p == (Y)->left_box_line_p \ && (X)->right_box_line_p == (Y)->right_box_line_p \ @@ -665,16 +673,18 @@ #define GLYPH_EQUAL_P(X, Y) \ #define GLYPH_CHAR_AND_FACE_EQUAL_P(X, Y) \ ((X)->u.ch == (Y)->u.ch \ && (X)->face_id == (Y)->face_id \ + && (X)->frame == (Y)->frame \ && (X)->padding_p == (Y)->padding_p) /* Fill a character glyph GLYPH. CODE, FACE_ID, PADDING_P correspond to the bits defined for the typedef `GLYPH' in lisp.h. */ -#define SET_CHAR_GLYPH(GLYPH, CODE, FACE_ID, PADDING_P) \ +#define SET_CHAR_GLYPH(FRAME, GLYPH, CODE, FACE_ID, PADDING_P) \ do \ { \ (GLYPH).u.ch = (CODE); \ (GLYPH).face_id = (FACE_ID); \ + (GLYPH).frame = (FRAME); \ (GLYPH).padding_p = (PADDING_P); \ } \ while (false) @@ -682,11 +692,9 @@ #define SET_CHAR_GLYPH(GLYPH, CODE, FACE_ID, PADDING_P) \ /* Fill a character type glyph GLYPH from a glyph typedef FROM as defined in lisp.h. */ -#define SET_CHAR_GLYPH_FROM_GLYPH(GLYPH, FROM) \ - SET_CHAR_GLYPH (GLYPH, \ - GLYPH_CHAR (FROM), \ - GLYPH_FACE (FROM), \ - false) +#define SET_CHAR_GLYPH_FROM_GLYPH(FRAME, GLYPH, FROM) \ + SET_CHAR_GLYPH (FRAME, GLYPH, GLYPH_CHAR (FROM), \ + GLYPH_FACE (FROM), false) /* Construct a glyph code from a character glyph GLYPH. If the character is multibyte, return -1 as we can't use glyph table for a @@ -3806,7 +3814,7 @@ #define IMAGE_BACKGROUND_TRANSPARENT(img, f, mask) \ void free_glyphs (struct frame *); void free_window_matrices (struct window *); void check_glyph_memory (void); -void mirrored_line_dance (struct glyph_matrix *, int, int, int *, char *); +void mirrored_line_dance (struct frame *f, int, int, int *, char *); void clear_glyph_matrix (struct glyph_matrix *); void clear_current_matrices (struct frame *f); void clear_desired_matrices (struct frame *); @@ -3830,7 +3838,7 @@ #define IMAGE_BACKGROUND_TRANSPARENT(img, f, mask) \ void init_display (void); void syms_of_display (void); extern void spec_glyph_lookup_face (struct window *, GLYPH *); -extern void fill_up_frame_row_with_spaces (struct glyph_row *, int); +extern void fill_up_frame_row_with_spaces (struct frame *, struct glyph_row *, int); /* Defined in terminal.c. */ @@ -3912,6 +3920,16 @@ #define IMAGE_BACKGROUND_TRANSPARENT(img, f, mask) \ #endif /* HAVE_WINDOW_SYSTEM */ +struct frame *root_frame (struct frame *f); +Lisp_Object frames_in_reverse_z_order (struct frame *f, bool visible); +bool is_tty_frame (struct frame *f); +bool is_tty_child_frame (struct frame *f); +bool is_tty_root_frame (struct frame *f); +bool combine_updates (Lisp_Object root_frames, bool force_p, bool inhibit_id_p); +bool combine_updates_for_frame (struct frame *f, bool force_p, bool inhibit_id_p); +void tty_raise_lower_frame (struct frame *f, bool raise); +int max_child_z_order (struct frame *parent); + INLINE_HEADER_END #endif /* not DISPEXTERN_H_INCLUDED */ modified src/dispnew.c @@ -42,6 +42,8 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2024 Free Software Foundation, #include "tparam.h" #include "xwidget.h" #include "pdumper.h" +#include "disptab.h" +#include "cm.h" #ifdef HAVE_ANDROID #include "android.h" @@ -71,7 +73,7 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2024 Free Software Foundation, /* Function prototypes. */ -static void update_frame_line (struct frame *, int, bool); +static void write_row (struct frame *f, int vpos, bool updating_menu_p); static int required_matrix_height (struct window *); static int required_matrix_width (struct window *); static void increment_row_positions (struct glyph_row *, ptrdiff_t, ptrdiff_t); @@ -80,9 +82,9 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2024 Free Software Foundation, static void build_frame_matrix_from_leaf_window (struct glyph_matrix *, struct window *); static void adjust_decode_mode_spec_buffer (struct frame *); -static void fill_up_glyph_row_with_spaces (struct glyph_row *); +static void fill_up_glyph_row_with_spaces (struct frame *, struct glyph_row *); static void clear_window_matrices (struct window *, bool); -static void fill_up_glyph_row_area_with_spaces (struct glyph_row *, int); +static void fill_up_glyph_row_area_with_spaces (struct frame *, struct glyph_row *, int); static int scrolling_window (struct window *, int); static bool update_window_line (struct window *, int, bool *); static void mirror_make_current (struct window *, int); @@ -93,13 +95,27 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2024 Free Software Foundation, static void mirror_line_dance (struct window *, int, int, int *, char *); static bool update_window_tree (struct window *, bool); static bool update_window (struct window *, bool); -static bool update_frame_1 (struct frame *, bool, bool, bool, bool); +static bool write_matrix (struct frame *, bool, bool, bool, bool); static bool scrolling (struct frame *); static void set_window_cursor_after_update (struct window *); static void adjust_frame_glyphs_for_window_redisplay (struct frame *); static void adjust_frame_glyphs_for_frame_redisplay (struct frame *); static void set_window_update_flags (struct window *w, bool on_p); +#if 0 /* Please leave this in as a debugging aid. */ +static void +check_rows (struct frame *f) +{ + for (int y = 0; y < f->desired_matrix->nrows; ++y) + if (MATRIX_ROW_ENABLED_P (f->desired_matrix, y)) + { + struct glyph_row *row = MATRIX_ROW (f->desired_matrix, y); + for (int x = 0; x < row->used[TEXT_AREA]; ++x) + eassert (row->glyphs[TEXT_AREA][x].frame != 0); + } +} +#endif + /* True means last display completed. False means it was preempted. */ bool display_completed; @@ -122,11 +138,6 @@ Copyright (C) 1985-1988, 1993-1995, 1997-2024 Free Software Foundation, #endif /* GLYPH_DEBUG and ENABLE_CHECKING */ -/* If non-null, the frame whose frame matrices are manipulated. If - null, window matrices are worked on. */ - -static struct frame *frame_matrix_frame; - /* Convert vpos and hpos from frame to window and vice versa. This may only be used for terminal frames. */ @@ -1178,7 +1189,15 @@ line_hash_code (struct frame *f, struct glyph_row *row) while (glyph < end) { int c = glyph->u.ch; - int face_id = glyph->face_id; + unsigned int face_id = glyph->face_id; + /* A given row of a frame glyph matrix could have glyphs + from more than one frame, if child frames are displayed. + Since face_id of a face depends on the frame (it's an + index into the frame's face cache), we need the hash + value to include something specific to the frame, and we + use the frame cache's address for that purpose. */ + if (glyph->frame && glyph->frame != f) + face_id += (uintptr_t) glyph->frame->face_cache; if (FRAME_MUST_WRITE_SPACES (f)) c -= SPACEGLYPH; hash = (((hash << 4) + (hash >> 24)) & 0x0fffffff) + c; @@ -1213,7 +1232,7 @@ line_draw_cost (struct frame *f, struct glyph_matrix *matrix, int vpos) if (!FRAME_MUST_WRITE_SPACES (f)) { /* Skip from the end over trailing spaces. */ - while (end > beg && CHAR_GLYPH_SPACE_P (*(end - 1))) + while (end > beg && CHAR_GLYPH_SPACE_P (f, *(end - 1))) --end; /* All blank line. */ @@ -1221,7 +1240,7 @@ line_draw_cost (struct frame *f, struct glyph_matrix *matrix, int vpos) return 0; /* Skip over leading spaces. */ - while (CHAR_GLYPH_SPACE_P (*beg)) + while (CHAR_GLYPH_SPACE_P (f, *beg)) ++beg; } @@ -2558,6 +2577,7 @@ build_frame_matrix_from_leaf_window (struct glyph_matrix *frame_matrix, struct w int window_y, frame_y; /* If non-zero, a glyph to insert at the right border of W. */ GLYPH right_border_glyph; + struct frame *f = XFRAME (w->frame); SET_GLYPH_FROM_CHAR (right_border_glyph, 0); @@ -2599,10 +2619,10 @@ build_frame_matrix_from_leaf_window (struct glyph_matrix *frame_matrix, struct w /* Fill up the frame row with spaces up to the left margin of the window row. */ - fill_up_frame_row_with_spaces (frame_row, window_matrix->matrix_x); + fill_up_frame_row_with_spaces (f, frame_row, window_matrix->matrix_x); /* Fill up areas in the window matrix row with spaces. */ - fill_up_glyph_row_with_spaces (window_row); + fill_up_glyph_row_with_spaces (f, window_row); /* If only part of W's desired matrix has been built, and window_row wasn't displayed, use the corresponding current @@ -2616,10 +2636,21 @@ build_frame_matrix_from_leaf_window (struct glyph_matrix *frame_matrix, struct w if (current_row_p) { - /* Copy window row to frame row. */ - memcpy (frame_row->glyphs[TEXT_AREA] + window_matrix->matrix_x, - window_row->glyphs[0], - window_matrix->matrix_w * sizeof (struct glyph)); + /* If the desired glyphs for this row haven't been built, + copy from the corresponding current row, but only if it + is enabled, because ottherwise its contents are invalid. */ + struct glyph *to = frame_row->glyphs[TEXT_AREA] + window_matrix->matrix_x; + struct glyph *from = window_row->glyphs[0]; + for (int i = 0; i < window_matrix->matrix_w; ++i) + { + if (window_row->enabled_p) + to[i] = from[i]; + else + { + to[i] = space_glyph; + to[i].frame = f; + } + } } else { @@ -2638,7 +2669,7 @@ build_frame_matrix_from_leaf_window (struct glyph_matrix *frame_matrix, struct w glyph with the vertical border glyph. */ eassert (border->type == CHAR_GLYPH); border->type = CHAR_GLYPH; - SET_CHAR_GLYPH_FROM_GLYPH (*border, right_border_glyph); + SET_CHAR_GLYPH_FROM_GLYPH (f, *border, right_border_glyph); } #ifdef GLYPH_DEBUG @@ -2701,11 +2732,11 @@ spec_glyph_lookup_face (struct window *w, GLYPH *glyph) To be called for frame-based redisplay, only. */ static void -fill_up_glyph_row_with_spaces (struct glyph_row *row) +fill_up_glyph_row_with_spaces (struct frame *f, struct glyph_row *row) { - fill_up_glyph_row_area_with_spaces (row, LEFT_MARGIN_AREA); - fill_up_glyph_row_area_with_spaces (row, TEXT_AREA); - fill_up_glyph_row_area_with_spaces (row, RIGHT_MARGIN_AREA); + fill_up_glyph_row_area_with_spaces (f, row, LEFT_MARGIN_AREA); + fill_up_glyph_row_area_with_spaces (f, row, TEXT_AREA); + fill_up_glyph_row_area_with_spaces (f, row, RIGHT_MARGIN_AREA); } @@ -2713,15 +2744,19 @@ fill_up_glyph_row_with_spaces (struct glyph_row *row) frame-based redisplay only. */ static void -fill_up_glyph_row_area_with_spaces (struct glyph_row *row, int area) +fill_up_glyph_row_area_with_spaces (struct frame *f, struct glyph_row *row, + int area) { if (row->glyphs[area] < row->glyphs[area + 1]) { struct glyph *end = row->glyphs[area + 1]; struct glyph *text = row->glyphs[area] + row->used[area]; - while (text < end) - *text++ = space_glyph; + for (; text < end; ++text) + { + *text = space_glyph; + text->frame = f; + } row->used[area] = text - row->glyphs[area]; } } @@ -2731,13 +2766,16 @@ fill_up_glyph_row_area_with_spaces (struct glyph_row *row, int area) reached. In frame matrices only one area, TEXT_AREA, is used. */ void -fill_up_frame_row_with_spaces (struct glyph_row *row, int upto) +fill_up_frame_row_with_spaces (struct frame *f, struct glyph_row *row, int upto) { int i = row->used[TEXT_AREA]; struct glyph *glyph = row->glyphs[TEXT_AREA]; - while (i < upto) - glyph[i++] = space_glyph; + for (; i < upto; ++i) + { + glyph[i] = space_glyph; + glyph[i].frame = f; + } row->used[TEXT_AREA] = i; } @@ -2748,17 +2786,6 @@ fill_up_frame_row_with_spaces (struct glyph_row *row, int upto) Mirroring operations on frame matrices in window matrices **********************************************************************/ -/* Set frame being updated via frame-based redisplay to F. This - function must be called before updates to make explicit that we are - working on frame matrices or not. */ - -static void -set_frame_matrix_frame (struct frame *f) -{ - frame_matrix_frame = f; -} - - /* Make sure glyph row ROW in CURRENT_MATRIX is up to date. DESIRED_MATRIX is the desired matrix corresponding to CURRENT_MATRIX. The update is done by exchanging glyph pointers @@ -2768,9 +2795,10 @@ set_frame_matrix_frame (struct frame *f) operations in window matrices of frame_matrix_frame. */ static void -make_current (struct glyph_matrix *desired_matrix, - struct glyph_matrix *current_matrix, int row) +make_current (struct frame *f, struct window *w, int row) { + struct glyph_matrix *desired_matrix = f ? f->desired_matrix : w->desired_matrix; + struct glyph_matrix *current_matrix = f ? f->current_matrix : w->current_matrix; struct glyph_row *current_row = MATRIX_ROW (current_matrix, row); struct glyph_row *desired_row = MATRIX_ROW (desired_matrix, row); bool mouse_face_p = current_row->mouse_face_p; @@ -2797,8 +2825,8 @@ make_current (struct glyph_matrix *desired_matrix, /* If we are called on frame matrices, perform analogous operations for window matrices. */ - if (frame_matrix_frame) - mirror_make_current (XWINDOW (frame_matrix_frame->root_window), row); + if (f) + mirror_make_current (XWINDOW (f->root_window), row); } @@ -2862,9 +2890,11 @@ mirror_make_current (struct window *w, int frame_row) This function is called from do_scrolling and do_direct_scrolling. */ void -mirrored_line_dance (struct glyph_matrix *matrix, int unchanged_at_top, int nlines, +mirrored_line_dance (struct frame *f, int unchanged_at_top, int nlines, int *copy_from, char *retained_p) { + struct glyph_matrix *matrix = f->current_matrix; + /* A copy of original rows. */ struct glyph_row *old_rows; @@ -2894,9 +2924,8 @@ mirrored_line_dance (struct glyph_matrix *matrix, int unchanged_at_top, int nlin } /* Do the same for window matrices, if MATRIX is a frame matrix. */ - if (frame_matrix_frame) - mirror_line_dance (XWINDOW (frame_matrix_frame->root_window), - unchanged_at_top, nlines, copy_from, retained_p); + mirror_line_dance (XWINDOW (f->root_window), + unchanged_at_top, nlines, copy_from, retained_p); SAFE_FREE (); } @@ -3194,7 +3223,10 @@ redraw_frame (struct frame *f) future. */ SET_FRAME_GARBAGED (f); - clear_frame (f); + /* clear_frame is actually a "clear_terminal", i.e. + it clears the entire screen. */ + if (!FRAME_PARENT_FRAME (f)) + clear_frame (f); clear_current_matrices (f); update_end (f); fset_redisplay (f); @@ -3229,145 +3261,797 @@ DEFUN ("redraw-display", Fredraw_display, Sredraw_display, 0, 0, "", return Qnil; } - -/*********************************************************************** - Frame Update - ***********************************************************************/ +/********************************************************************** + TTY Child Frames + **********************************************************************/ -/* Update frame F based on the data in desired matrices. +/* Child frames on ttys break the assumption that frames on a tty + always occupy the whole terminal. They can overlap instead. - If FORCE_P, don't let redisplay be stopped by detecting pending input. - If INHIBIT_HAIRY_ID_P, don't try scrolling. + Let a "root" frame be a frame that has no parent frame. Such root + frames are required to be the size of the terminal screen. The + current glyph matrix of a root frame of a termimnal represents what + is on the screen. The desired matrix of a root frame represents + what should be one the screen. - Value is true if redisplay was stopped due to pending input. */ + Building the desired matrix of root frame proceeds by + + - building the desired matrix of the root frame itself which is + the bottommost frame in z-order; + - building desired matrices of child frames in z-order, topmost last; + - copying the desired glyphs from child frames to the desired glyphs + of the root frame + + Updating the screen is then done using root frame matrices as it + was before child frames were introduced. Child frame's current + matrices are updated by copying glyph contents of the current + matrix of the root frames to the current matrices of child + frames. This implicitly also updates the glyph contents of their + windows' current matrices. */ + +struct rect +{ + int x, y, w, h; +}; + +/* Compute the intersection of R1 and R2 in R. Value is true if R1 and + R2 intersect, false otherwise. */ + +static bool +rect_intersect (struct rect *r, struct rect r1, struct rect r2) +{ + int x1 = max (r1.x, r2.x); + int x2 = min (r1.x + r1.w, r2.x + r2.w); + if (x2 < x1) + return false; + int y1 = max (r1.y, r2.y); + int y2 = min (r1.y + r1.h, r2.y + r2.h); + if (y2 < y1) + return false; + *r = (struct rect) { .x = x1, .y = y1, .w = x2 - x1, .h = y2 - y1 }; + return true; +} + +/* Return the absolute position of frame F in *X and *Y. */ + +static void +frame_pos_abs (struct frame *f, int *x, int *y) +{ + *x = *y = 0; + for (; f; f = FRAME_PARENT_FRAME (f)) + { + *x += f->left_pos; + *y += f->top_pos; + } +} + +/* Return the rectangle frame F occupies. X and Y are in absolute + coordinates. */ + +static struct rect +frame_rect_abs (struct frame *f) +{ + int x, y; + frame_pos_abs (f, &x, &y); + return (struct rect) { x, y, f->total_cols, f->total_lines }; +} + +/* Return the root frame of frame F. Follow the parent_frame chain + until we reach a frame that has no parent. That is the root frame. + Note that the root of a root frame is itself. */ + +struct frame * +root_frame (struct frame *f) +{ + while (FRAME_PARENT_FRAME (f)) + f = FRAME_PARENT_FRAME (f); + return f; +} + +int +max_child_z_order (struct frame *parent) +{ + Lisp_Object tail, frame; + int z_order = 0; + FOR_EACH_FRAME (tail, frame) + { + struct frame *f = XFRAME (frame); + if (FRAME_PARENT_FRAME (f) == parent) + z_order = max (z_order, f->z_order); + } + return z_order; +} + +/* Return true if F1 is an ancestor of F2. */ + +static bool +is_frame_ancestor (struct frame *f1, struct frame *f2) +{ + for (struct frame *f = FRAME_PARENT_FRAME (f2); f; f = FRAME_PARENT_FRAME (f)) + if (f == f1) + return true; + return false; +} + +/* Return a list of all frames having root frame ROOT. + If VISIBLE_ONLY is true, return only visible frames. */ + +static Lisp_Object +frames_with_root (struct frame *root, bool visible_only) +{ + Lisp_Object list = Qnil; + Lisp_Object tail, frame; + FOR_EACH_FRAME (tail, frame) + { + struct frame *f = XFRAME (frame); + if (root_frame (f) == root + && (!visible_only || FRAME_VISIBLE_P (f))) + list = Fcons (frame, list); + } + return list; +} + +/* Return a list of frames having parent frame PARENT. + If VISIBLE_ONLY is true, return only visible frames. */ + +static Lisp_Object +frames_with_parent (struct frame *parent, bool visible_only) +{ + Lisp_Object list = Qnil; + Lisp_Object tail, frame; + FOR_EACH_FRAME (tail, frame) + { + struct frame *f = XFRAME (frame); + if (FRAME_PARENT_FRAME (f) == parent + && (!visible_only || FRAME_VISIBLE_P (f))) + list = Fcons (frame, list); + } + return list; +} + +/* Compare frames F1 and F2 for z-order. Value is like strcmp. */ + +static int +frame_z_order_cmp (struct frame *f1, struct frame *f2) +{ + if (f1 == f2) + return 0; + if (is_frame_ancestor (f1, f2)) + return -1; + if (is_frame_ancestor (f2, f1)) + return 1; + return f1->z_order - f2->z_order; +} + +DEFUN ("frame--z-order-lessp", Fframe__z_order_lessp, Sframe__z_order_lessp, + 2, 2, 0, doc: /* Internal frame sorting function A < B. */) + (Lisp_Object a, Lisp_Object b) +{ + eassert (FRAMEP (a) && FRAMEP (b)); + return frame_z_order_cmp (XFRAME (a), XFRAME (b)) < 0 ? Qt : Qnil; +} + +/* Return a z-order list of frames with the same root as F. The list + is ordered topmost frame last. Note that this list contains + the root frame of F itself as first element. */ + +Lisp_Object +frames_in_reverse_z_order (struct frame *f, bool visible_only) +{ + struct frame *root = root_frame (f); + Lisp_Object frames = frames_with_root (root, visible_only); + frames = CALLN (Fsort, frames, QClessp, Qframe__z_order_lessp); + eassert (FRAMEP (XCAR (frames))); + eassert (XFRAME (XCAR (frames)) == root); + return frames; +} + +/* Raise of lower frame F in z-order. If RAISE is true, raise F, else + lower f. */ + +void +tty_raise_lower_frame (struct frame *f, bool raise) +{ + struct frame *parent = FRAME_PARENT_FRAME (f); + if (parent == NULL) + return; + + Lisp_Object siblings = frames_with_parent (parent, false); + siblings = CALLN (Fsort, siblings, QClessp, Qframe__z_order_lessp); + + int i = 0; + for (Lisp_Object tail = siblings; CONSP (tail); tail = XCDR (tail)) + { + struct frame *child = XFRAME (XCAR (tail)); + if (child != f) + child->z_order = i++; + } + f->z_order = raise ? i : 0; +} + +/* Return true if frame F is a tty frame. */ bool -update_frame (struct frame *f, bool force_p, bool inhibit_hairy_id_p) +is_tty_frame (struct frame *f) { - /* True means display has been paused because of pending input. */ - bool paused_p; - struct window *root_window = XWINDOW (f->root_window); + return FRAME_TERMCAP_P (f); +} - if (redisplay_dont_pause) - force_p = true; - else if (!force_p && detect_input_pending_ignore_squeezables ()) +/* Return true if frame F is a tty child frame. */ + +bool +is_tty_child_frame (struct frame *f) +{ + return FRAME_PARENT_FRAME (f) && is_tty_frame (f); +} + +/* Return true if frame F is a tty root frame. */ + +bool +is_tty_root_frame (struct frame *f) +{ + return !FRAME_PARENT_FRAME (f) && is_tty_frame (f); +} + +/* Return the index of the first enabled row in MATRIX, or -1 if there + is none. */ + +static int +first_enabled_row (struct glyph_matrix *matrix) +{ + for (int i = 0; i < matrix->nrows; ++i) + if (MATRIX_ROW_ENABLED_P (matrix, i)) + return i; + return -1; +} + +/* On tty frame F, make desired matrix current, without writing + to the terminal. */ + +static void +make_matrix_current (struct frame *f) +{ + int first_row = first_enabled_row (f->desired_matrix); + if (first_row >= 0) + for (int i = first_row; i < f->desired_matrix->nrows; ++i) + if (MATRIX_ROW_ENABLED_P (f->desired_matrix, i)) + make_current (f, NULL, i); +} + +/* Prepare ROOT's desired row at index Y for copying child frame + contents to it. Value is the prepared desired row or NULL if we + don't have, and can't contruct a desired row. */ + +static struct glyph_row * +prepare_desired_root_row (struct frame *root, int y) +{ + /* If we have a desired row that has been displayed, use that. */ + struct glyph_row *desired_row = MATRIX_ROW (root->desired_matrix, y); + if (desired_row->enabled_p) + return desired_row; + + /* If we have a current row that is up to date, copy that to the + desired row and use that. */ + /* Don't copy rows that aren't enabled, in particuler because they + might not have the 'frame' member of glyphs set. */ + struct glyph_row *current_row = MATRIX_ROW (root->current_matrix, y); + if (current_row->enabled_p) { - paused_p = true; - goto do_pause; + memcpy (desired_row->glyphs[0], current_row->glyphs[0], + root->current_matrix->matrix_w * sizeof (struct glyph)); + desired_row->enabled_p = true; + return desired_row; } - if (FRAME_WINDOW_P (f)) + return NULL; +} + +/* Change GLYPH to be a space glyph. */ + +static void +make_glyph_space (struct glyph *glyph) +{ + glyph->u.ch = ' '; + glyph->pixel_width = 1; + glyph->padding_p = 0; +} + +/* On root frame ROOT, if the glyph in ROW at position X is part of a + sequence of glyphs for a wide character, change every glyph belonging + to the sequence to a space. If X is outside of ROOT, do nothing. */ + +static void +neutralize_wide_char (struct frame *root, struct glyph_row *row, int x) +{ + if (x < 0 || x >= root->desired_matrix->matrix_w) + return; + + struct glyph *glyph = row->glyphs[TEXT_AREA] + x; + if (glyph->type == CHAR_GLYPH && CHARACTER_WIDTH (glyph->u.ch) > 1) { - /* We are working on window matrix basis. All windows whose - flag must_be_updated_p is set have to be updated. */ + /* Glyph is somewhere in a sequence of glyphs for a wide + character, find the start. */ + struct glyph *row_start = row->glyphs[TEXT_AREA]; + while (glyph > row_start && glyph->padding_p) + --glyph; - /* Record that we are not working on frame matrices. */ - set_frame_matrix_frame (NULL); + /* Make everything in the sequence a space glyph. */ + eassert (!glyph->padding_p); + make_glyph_space (glyph); + struct glyph *row_limit = row_start + row->used[TEXT_AREA]; + for (++glyph; glyph < row_limit && glyph->padding_p; ++glyph) + make_glyph_space (glyph); + } +} - /* Update all windows in the window tree of F, maybe stopping - when pending input is detected. */ - update_begin (f); +/* Produce glyphs for box character BOX in ROW. X is the position in + ROW where to start producing glyphs. N is the number of glyphs to + produce. CHILD is the frame to use for the face of the glyphs. */ -#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR - /* Update the menu bar on X frames that don't have toolkit - support. */ - if (WINDOWP (f->menu_bar_window)) - update_window (XWINDOW (f->menu_bar_window), true); -#endif +static void +produce_box_glyphs (enum box box, struct glyph_row *row, int x, int n, + struct frame *child) +{ + int dflt; + switch (box) + { + case BOX_VERTICAL: + dflt = '|'; + break; + case BOX_HORIZONTAL: + dflt = '-'; + break; + case BOX_DOWN_RIGHT: + case BOX_DOWN_LEFT: + case BOX_UP_RIGHT: + case BOX_UP_LEFT: + dflt = '+'; + break; + } -#if defined (HAVE_WINDOW_SYSTEM) - /* Update the tab-bar window, if present. */ - if (WINDOWP (f->tab_bar_window)) + /* FIXME/tty: some face for the border. */ + int face_id = BORDER_FACE_ID; + GLYPH g; + SET_GLYPH (g, dflt, face_id); + + if (DISP_TABLE_P (Vstandard_display_table)) + { + struct Lisp_Char_Table *dp = XCHAR_TABLE (Vstandard_display_table); + Lisp_Object gc = dp->extras[box]; + if (GLYPH_CODE_P (gc)) { - struct window *w = XWINDOW (f->tab_bar_window); + SET_GLYPH_FROM_GLYPH_CODE (g, gc); + /* Sorry, but I really don't care if the glyph has a face :-). */ + } + } - /* Update tab-bar window. */ - if (w->must_be_updated_p) - { - Lisp_Object tem; + struct glyph *glyph = row->glyphs[0] + x; + for (int i = 0; i < n; ++i, ++glyph) + { + glyph->type = CHAR_GLYPH; + glyph->u.ch = GLYPH_CHAR (g); + glyph->charpos = -1; + glyph->pixel_width = 1; + glyph->multibyte_p = 1; + glyph->face_id = GLYPH_FACE (g); + glyph->frame = child; + glyph->padding_p = 0; + glyph->object = Qnil; + glyph->padding_p = 0; + } +} - update_window (w, true); - w->must_be_updated_p = false; +/* Produce box glyphs LEFT and RIGHT in ROOT_ROW. X and W are the start + and width of a range in ROOT_ROW before and after which to put the + box glyphs, if they fit. ROOT and CHILD are root and child frame we + are working on. ROOT is the root frame whose matrix dimensions + determines if the box glyphs fit. CHILD is the frame whose faces to + use for the box glyphs. */ - /* Swap tab-bar strings. We swap because we want to - reuse strings. */ - tem = f->current_tab_bar_string; - fset_current_tab_bar_string (f, f->desired_tab_bar_string); - fset_desired_tab_bar_string (f, tem); - } +static void +produce_box_sides (enum box left, enum box right, struct glyph_row *root_row, int x, + int w, struct frame *root, struct frame *child) +{ + if (x > 0) + { + neutralize_wide_char (root, root_row, x - 1); + produce_box_glyphs (left, root_row, x - 1, 1, child); + } + + if (x + w < root->desired_matrix->matrix_w) + { + neutralize_wide_char (root, root_row, x + w); + produce_box_glyphs (right, root_row, x + w, 1, child); + } +} + +static void +produce_box_line (struct frame *root, struct frame *child, int x, int y, int w, + bool first) +{ + struct glyph_row *root_row = prepare_desired_root_row (root, y); + if (root_row == NULL) + return; + if (first) + produce_box_sides (BOX_DOWN_RIGHT, BOX_DOWN_LEFT, root_row, x, w, root, child); + else + produce_box_sides (BOX_UP_RIGHT, BOX_UP_LEFT, root_row, x, w, root, child); + produce_box_glyphs (BOX_HORIZONTAL, root_row, x, w, child); + root_row->hash = row_hash (root_row); +} + +/* Copy to ROOT's desired matrix what we need from CHILD. */ + +static void +copy_child_glyphs (struct frame *root, struct frame *child) +{ + eassert (!FRAME_PARENT_FRAME (root)); + eassert (is_frame_ancestor (root, child)); + + /* Determine the intersection of the child frame rectangle with the + root frame. This is basically clipping the child frame to the + root frame rectangle. */ + struct rect r; + if (!rect_intersect (&r, frame_rect_abs (root), frame_rect_abs (child))) + return; + + /* Build CHILD's current matrix which we need to copy from it. */ + make_matrix_current (child); + + /* Draw borders around the child frame. */ + if (!FRAME_UNDECORATED (child)) + { + /* Horizontal line above. */ + if (r.y > 0) + produce_box_line (root, child, r.x, r.y - 1, r.w, true); + + for (int y = r.y; y < r.y + r.h; ++y) + { + struct glyph_row *root_row = prepare_desired_root_row (root, y); + if (root_row) + produce_box_sides (BOX_VERTICAL, BOX_VERTICAL, root_row, r.x, r.w, + root, child); } -#endif -#if defined (HAVE_WINDOW_SYSTEM) && ! defined (HAVE_EXT_TOOL_BAR) - /* Update the tool-bar window, if present. */ - if (WINDOWP (f->tool_bar_window)) + /* Horizontal line below. */ + if (r.y + r.h < root->desired_matrix->matrix_h) + produce_box_line (root, child, r.x, r.y + r.h, r.w, false); + } + + /* First visible row/col, relative to the child frame. */ + int child_x = child->left_pos < 0 ? - child->left_pos : 0; + int child_y = child->top_pos < 0 ? - child->top_pos : 0; + + /* For all rows in the intersection, copy glyphs from the child's + current matrix to the root's desired matrix, enabling those rows + if they aren't already. */ + for (int y = r.y; y < r.y + r.h; ++y, ++child_y) + { + struct glyph_row *root_row = prepare_desired_root_row (root, y); + if (root_row == NULL) + continue; + + /* Deal with wide characters unless already done as part of + drawing a box around the child frame. */ + if (FRAME_UNDECORATED (child)) { - struct window *w = XWINDOW (f->tool_bar_window); + neutralize_wide_char (root, root_row, r.x - 1); + neutralize_wide_char (root, root_row, r.x + r.w); + } - /* Update tool-bar window. */ - if (w->must_be_updated_p) - { - Lisp_Object tem; + /* Copy what's visible from the child's current row. If that row + is not enabled_p, we can't copy anything that makes sense. */ + struct glyph_row *child_row = MATRIX_ROW (child->current_matrix, child_y); + if (child_row->enabled_p) + memcpy (root_row->glyphs[0] + r.x, child_row->glyphs[0] + child_x, + r.w * sizeof (struct glyph)); - update_window (w, true); - w->must_be_updated_p = false; + /* Compute a new hash since we changed glyphs. */ + root_row->hash = row_hash (root_row); + } +} - /* Swap tool-bar strings. We swap because we want to - reuse strings. */ - tem = f->current_tool_bar_string; - fset_current_tool_bar_string (f, f->desired_tool_bar_string); - fset_desired_tool_bar_string (f, tem); - } +/*********************************************************************** + Frame Update + ***********************************************************************/ + +/* Update the menu bar on X frames that don't have toolkit + support. */ + +static void +update_menu_bar (struct frame *f) +{ +#if defined HAVE_WINDOW_SYSTEM && !defined HAVE_EXT_MENU_BAR + if (WINDOWP (f->menu_bar_window)) + update_window (XWINDOW (f->menu_bar_window), true); +#endif +} + +#ifdef HAVE_WINDOW_SYSTEM +static void +update_bar_window (Lisp_Object window, Lisp_Object *current, + Lisp_Object *desired) +{ + if (WINDOWP (window)) + { + struct window *w = XWINDOW (window); + if (w->must_be_updated_p) + { + update_window (w, true); + w->must_be_updated_p = false; + Lisp_Object tem = *current; + *current = *desired; + *desired = tem; } + } +} #endif - /* Update windows. */ - paused_p = update_window_tree (root_window, force_p); - update_end (f); +/* Update the tab-bar window of frame F, if present. */ + +static void +update_tab_bar (struct frame *f) +{ +#if defined(HAVE_WINDOW_SYSTEM) + update_bar_window (f->tab_bar_window, &f->current_tab_bar_string, + &f->desired_tab_bar_string); +#endif +} + +static void +update_tool_bar (struct frame *f) +{ +#if defined(HAVE_WINDOW_SYSTEM) && !defined(HAVE_EXT_TOOL_BAR) + update_bar_window (f->tool_bar_window, &f->current_tool_bar_string, + &f->desired_tool_bar_string); +#endif +} + +static bool +update_window_frame (struct frame *f, bool force_p) +{ + eassert (FRAME_WINDOW_P (f)); + update_begin (f); + update_menu_bar (f); + update_tab_bar (f); + update_tool_bar (f); + struct window *root_window = XWINDOW (f->root_window); + bool paused_p = update_window_tree (root_window, force_p); + update_end (f); + set_window_update_flags (root_window, false); + return paused_p; +} + +static bool +update_initial_frame (struct frame *f, bool force_p) +{ + build_frame_matrix (f); + struct window *root_window = XWINDOW (f->root_window); + set_window_update_flags (root_window, false); + return false; +} + +static void +flush_terminal (struct frame *f) +{ + if (FRAME_TTY (f)->termscript) + fflush (FRAME_TTY (f)->termscript); + fflush (FRAME_TTY (f)->output); +} + +static bool +update_tty_frame (struct frame *f, bool force_p) +{ + build_frame_matrix (f); + return false; +} + +/* Return the cursor position of the selected window of frame F, in + absolute coordinates in *X and *Y. Note that if F is a child frame, + its cursor may be clipped, i.e. outside of the bounds of the terminal + window. Value is false if the selected window of F doesn't have + valid cursor position info. */ + +static bool +abs_cursor_pos (struct frame *f, int *x, int *y) +{ + struct window *w = XWINDOW (f->selected_window); + if (w->cursor.vpos >= 0 + /* The cursor vpos may be temporarily out of bounds + in the following situation: There is one window, + with the cursor in the lower half of it. The window + is split, and a message causes a redisplay before + a new cursor position has been computed. */ + && w->cursor.vpos < WINDOW_TOTAL_LINES (w)) + { + int wx = WINDOW_TO_FRAME_HPOS (w, w->cursor.hpos); + int wy = WINDOW_TO_FRAME_VPOS (w, w->cursor.vpos); + + wx += max (0, w->left_margin_cols); + + int fx, fy; + frame_pos_abs (f, &fx, &fy); + *x = fx + wx; + *y = fy + wy; + return true; } - else + + *x = *y = 0; + return false; +} + +static bool +is_in_matrix (struct frame *f, int x, int y) +{ + struct frame *root = root_frame (f); + if (x < 0 || x >= root->current_matrix->matrix_w || y < 0 + || y >= root->current_matrix->matrix_h) + return false; + return true; +} + +/* Is the terminal cursor of the selected frame obscured by a child + frame? */ + +static bool +is_cursor_obscured (void) +{ + /* Give up if we can't tell where the cursor currently is. */ + int x, y; + if (!abs_cursor_pos (SELECTED_FRAME (), &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 (); +} + +/* Decide where to show the cursor, and whether to hide it. + + This works very well for Vertico-Posframe, Transient-Posframe and + Corfu, but it's debatable if it's the right thing for a general use + of child frames of all sorts, nested and so on. But it is also + debatable if that's a realistic use case from my POV. */ + +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 ()) + tty_hide_cursor (FRAME_TTY (root)); + + /* If the terminal cursor is not in the topmost child, the topmost + child's tty-cursor-if-topmost determines what to do. If it is + 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 ()) { - /* We are working on frame matrix basis. Set the frame on whose - frame matrix we operate. */ - set_frame_matrix_frame (f); + Lisp_Object frame; + XSETFRAME (frame, topmost_child); - /* Build F's desired matrix from window matrices. */ - build_frame_matrix (f); + int x, y; + Lisp_Object cursor = Fframe_parameter (frame, Qtty_non_selected_cursor); + if (!NILP (cursor) && abs_cursor_pos (topmost_child, &x, &y)) + { + if (is_in_matrix (root, x, y)) + { + cursor_to (root, y, x); + tty_show_cursor (FRAME_TTY (topmost_child)); + } + else + tty_hide_cursor (FRAME_TTY (root)); + } + } +} - /* Update the display. */ - if (FRAME_INITIAL_P (f)) - /* No actual display to update so the "update" is a nop and - obviously isn't interrupted by pending input. */ - paused_p = false; - else - { - update_begin (f); - paused_p = update_frame_1 (f, force_p, inhibit_hairy_id_p, 1, false); - update_end (f); - } - - if (FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) - { - if (FRAME_TTY (f)->termscript) - fflush (FRAME_TTY (f)->termscript); - if (FRAME_TERMCAP_P (f)) - fflush (FRAME_TTY (f)->output); - } - - /* Check window matrices for lost pointers. */ +bool +combine_updates_for_frame (struct frame *f, bool force_p, bool inhibit_scrolling) +{ + struct frame *root = root_frame (f); + eassert (FRAME_VISIBLE_P (root)); + + /* Process child frames in reverse z-order, topmost last. For each + child, copy what we need to the root's desired matrix. */ + Lisp_Object z_order = frames_in_reverse_z_order (root, true); + struct frame *topmost_child = NULL; + for (Lisp_Object tail = XCDR (z_order); CONSP (tail); tail = XCDR (tail)) + { + topmost_child = XFRAME (XCAR (tail)); + copy_child_glyphs (root, topmost_child); + } + + update_begin (root); + bool paused = write_matrix (root, force_p, inhibit_scrolling, 1, false); + if (!paused) + make_matrix_current (root); + update_end (root); + + /* If a child is displayed, and the cursor is displayed in another + frame, the child might lay above the cursor, so that it appers to + "shine through" the child. Avoid that because it's confusing. */ + if (topmost_child) + terminal_cursor_magic (root, topmost_child); + flush_terminal (root); + + for (Lisp_Object tail = z_order; CONSP (tail); tail = XCDR (tail)) + { + struct frame *f = XFRAME (XCAR (tail)); + struct window *root_window = XWINDOW (f->root_window); + set_window_update_flags (root_window, false); + clear_desired_matrices (f); #ifdef GLYPH_DEBUG check_window_matrix_pointers (root_window); - add_frame_display_history (f, paused_p); + add_frame_display_history (f, false); #endif } - do_pause: - /* Reset flags indicating that a window should be updated. */ - set_window_update_flags (root_window, false); + return paused; +} - display_completed = !paused_p; - return paused_p; +/* Update on the screen all root frames ROOTS. Called from + redisplay_internal as the last step of redisplaying. */ + +bool +combine_updates (Lisp_Object roots, bool force_p, bool inhibit_scrolling) +{ + if (redisplay_dont_pause) + force_p = true; + + for (; CONSP (roots); roots = XCDR (roots)) + { + struct frame *root = XFRAME (XCAR (roots)); + if (combine_updates_for_frame (root, force_p, inhibit_scrolling)) + { + display_completed = false; + return true; + } + } + + display_completed = true; + return false; +} + +/* Update frame F based on the data in desired matrices. + + If FORCE_P, don't let redisplay be stopped by detecting pending input. + If INHIBIT_SCROLLING, don't try scrolling. + + Value is true if redisplay was stopped due to pending input. */ + +bool +update_frame (struct frame *f, bool force_p, bool inhibit_scrolling) +{ + struct window *root_window = XWINDOW (f->root_window); + + if (redisplay_dont_pause) + force_p = true; + else if (!force_p && detect_input_pending_ignore_squeezables ()) + { + /* Reset flags indicating that a window should be updated. */ + set_window_update_flags (root_window, false); + display_completed = false; + return true; + } + + bool paused; + if (FRAME_WINDOW_P (f)) + paused = update_window_frame (f, force_p); + else if (FRAME_INITIAL_P (f)) + paused = update_initial_frame (f, force_p); + else + paused = update_tty_frame (f, force_p); + + if (paused) + display_completed = false; + return paused; } /* Update a TTY frame F that has a menu dropped down over some of its @@ -3384,29 +4068,25 @@ update_frame (struct frame *f, bool force_p, bool inhibit_hairy_id_p) update_frame_with_menu (struct frame *f, int row, int col) { struct window *root_window = XWINDOW (f->root_window); - bool paused_p, cursor_at_point_p; + bool cursor_at_point_p; eassert (FRAME_TERMCAP_P (f)); - /* We are working on frame matrix basis. Set the frame on whose - frame matrix we operate. */ - set_frame_matrix_frame (f); - /* Update the display. */ update_begin (f); cursor_at_point_p = !(row >= 0 && col >= 0); - /* Force update_frame_1 not to stop due to pending input, and not - try scrolling. */ - paused_p = update_frame_1 (f, 1, 1, cursor_at_point_p, true); + /* Do not stop due to pending input, and do not try scrolling. This + means that write_glyphs will always return false. */ + write_matrix (f, 1, 1, cursor_at_point_p, true); + make_matrix_current (f); + clear_desired_matrices (f); /* ROW and COL tell us where in the menu to position the cursor, so that screen readers know the active region on the screen. */ if (!cursor_at_point_p) cursor_to (f, row, col); update_end (f); + flush_terminal (f); - if (FRAME_TTY (f)->termscript) - fflush (FRAME_TTY (f)->termscript); - fflush (FRAME_TTY (f)->output); /* Check window matrices for lost pointers. */ #if GLYPH_DEBUG #if 0 @@ -3415,12 +4095,12 @@ update_frame_with_menu (struct frame *f, int row, int col) making any updates to the window matrices. */ check_window_matrix_pointers (root_window); #endif - add_frame_display_history (f, paused_p); + add_frame_display_history (f, false); #endif /* Reset flags indicating that a window should be updated. */ set_window_update_flags (root_window, false); - display_completed = !paused_p; + display_completed = true; } /* Update the mouse position for a frame F. This handles both @@ -3453,19 +4133,20 @@ update_mouse_position (struct frame *f, int x, int y) } DEFUN ("display--update-for-mouse-movement", Fdisplay__update_for_mouse_movement, - Sdisplay__update_for_mouse_movement, 2, 2, 0, + Sdisplay__update_for_mouse_movement, 3, 3, 0, doc: /* Handle mouse movement detected by Lisp code. This function should be called when Lisp code detects the mouse has moved, even if `track-mouse' is nil. This handles updates that do not rely on input events such as updating display for mouse-face properties or updating the help echo text. */) - (Lisp_Object mouse_x, Lisp_Object mouse_y) + (Lisp_Object mouse_frame, Lisp_Object mouse_x, Lisp_Object mouse_y) { + CHECK_FRAME (mouse_frame); CHECK_FIXNUM (mouse_x); CHECK_FIXNUM (mouse_y); - update_mouse_position (SELECTED_FRAME (), XFIXNUM (mouse_x), + update_mouse_position (XFRAME (mouse_frame), XFIXNUM (mouse_x), XFIXNUM (mouse_y)); return Qnil; } @@ -3507,9 +4188,6 @@ update_single_window (struct window *w) { struct frame *f = XFRAME (WINDOW_FRAME (w)); - /* Record that this is not a frame-based redisplay. */ - set_frame_matrix_frame (NULL); - /* Update W. */ update_begin (f); update_window (w, true); @@ -3688,6 +4366,8 @@ update_window (struct window *w, bool force_p) #ifdef HAVE_WINDOW_SYSTEM gui_update_window_begin (w); +#else + (void) changed_p; #endif yb = window_text_bottom_y (w); row = MATRIX_ROW (desired_matrix, 0); @@ -4321,7 +5001,7 @@ update_window_line (struct window *w, int vpos, bool *mouse_face_overwritten_p) /* Update current_row from desired_row. */ was_stipple = current_row->stipple_p; - make_current (w->desired_matrix, w->current_matrix, vpos); + make_current (NULL, w, vpos); /* If only a partial update was performed, any stipple already displayed in MATRIX_ROW (w->current_matrix, vpos) might still be @@ -4931,7 +5611,92 @@ scrolling_window (struct window *w, int tab_line_p) Frame-Based Updates ************************************************************************/ -/* Update the desired frame matrix of frame F. +static void +tty_set_cursor (void) +{ + struct frame *f = SELECTED_FRAME (); + + if ((cursor_in_echo_area + /* If we are showing a message instead of the mini-buffer, + show the cursor for the message instead of for the + (now hidden) mini-buffer contents. */ + || (BASE_EQ (minibuf_window, selected_window) + && BASE_EQ (minibuf_window, echo_area_window) + && !NILP (echo_area_buffer[0]))) + /* These cases apply only to the frame that contains + the active mini-buffer window. */ + && FRAME_HAS_MINIBUF_P (f) + && BASE_EQ (FRAME_MINIBUF_WINDOW (f), echo_area_window)) + { + int top = WINDOW_TOP_EDGE_LINE (XWINDOW (FRAME_MINIBUF_WINDOW (f))); + int col; + + /* Put cursor at the end of the prompt. If the mini-buffer + is several lines high, find the last line that has + any text on it. */ + int row = FRAME_TOTAL_LINES (f); + do + { + row--; + col = 0; + + if (MATRIX_ROW_ENABLED_P (f->current_matrix, row)) + { + /* Frame rows are filled up with spaces that + must be ignored here. */ + struct glyph_row *r = MATRIX_ROW (f->current_matrix, row); + struct glyph *start = r->glyphs[TEXT_AREA]; + + col = r->used[TEXT_AREA]; + while (0 < col && start[col - 1].charpos < 0) + col--; + } + } + while (row > top && col == 0); + + /* We exit the loop with COL at the glyph _after_ the last one. */ + if (col > 0) + col--; + + /* Make sure COL is not out of range. */ + if (col >= FRAME_CURSOR_X_LIMIT (f)) + { + /* If we have another row, advance cursor into it. */ + if (row < FRAME_TOTAL_LINES (f) - 1) + { + col = FRAME_LEFT_SCROLL_BAR_COLS (f); + row++; + } + /* Otherwise move it back in range. */ + else + col = FRAME_CURSOR_X_LIMIT (f) - 1; + } + + cursor_to (f, row, col); + } + else + { + /* We have only one cursor on terminal frames. Use it to + display the cursor of the selected window. */ + struct window *w = XWINDOW (FRAME_SELECTED_WINDOW (f)); + if (w->cursor.vpos >= 0 + /* The cursor vpos may be temporarily out of bounds + in the following situation: There is one window, + with the cursor in the lower half of it. The window + is split, and a message causes a redisplay before + a new cursor position has been computed. */ + && w->cursor.vpos < WINDOW_TOTAL_LINES (w)) + { + int x = WINDOW_TO_FRAME_HPOS (w, w->cursor.hpos); + int y = WINDOW_TO_FRAME_VPOS (w, w->cursor.vpos); + + x += max (0, w->left_margin_cols); + cursor_to (f, y, x); + } + } +} + +/* Write desired matix of tty frame F and make it current. FORCE_P means that the update should not be stopped by pending input. INHIBIT_ID_P means that scrolling by insert/delete should not be tried. @@ -4940,167 +5705,58 @@ scrolling_window (struct window *w, int tab_line_p) Value is true if update was stopped due to pending input. */ static bool -update_frame_1 (struct frame *f, bool force_p, bool inhibit_id_p, - bool set_cursor_p, bool updating_menu_p) +write_matrix (struct frame *f, bool force_p, bool inhibit_id_p, + bool set_cursor_p, bool updating_menu_p) { - /* Frame matrices to work on. */ - struct glyph_matrix *current_matrix = f->current_matrix; - struct glyph_matrix *desired_matrix = f->desired_matrix; - int i; - bool pause_p; - int preempt_count = clip_to_bounds (1, baud_rate / 2400 + 1, INT_MAX); - - eassert (current_matrix && desired_matrix); - - if (baud_rate != FRAME_COST_BAUD_RATE (f)) - calculate_costs (f); - if (!force_p && detect_input_pending_ignore_squeezables ()) - { - pause_p = 1; - goto do_pause; - } + return true; /* If we cannot insert/delete lines, it's no use trying it. */ if (!FRAME_LINE_INS_DEL_OK (f)) - inhibit_id_p = 1; + inhibit_id_p = true; - /* See if any of the desired lines are enabled; don't compute for - i/d line if just want cursor motion. */ - for (i = 0; i < desired_matrix->nrows; i++) - if (MATRIX_ROW_ENABLED_P (desired_matrix, i)) - break; + if (baud_rate != FRAME_COST_BAUD_RATE (f)) + calculate_costs (f); - /* Try doing i/d line, if not yet inhibited. */ - if (!inhibit_id_p && i < desired_matrix->nrows) + /* See if any of the desired lines are enabled; don't compute for + i/d line if just want cursor motion. */ + int first_row = first_enabled_row (f->desired_matrix); + if (!inhibit_id_p && first_row >= 0) force_p |= scrolling (f); - /* Update the individual lines as needed. Do bottom line first. */ - if (MATRIX_ROW_ENABLED_P (desired_matrix, desired_matrix->nrows - 1)) - update_frame_line (f, desired_matrix->nrows - 1, updating_menu_p); + /* Update the individual lines as needed. Do bottom line first. This + is done so that messages are made visible when pausing. */ + int last_row = f->desired_matrix->nrows - 1; + if (MATRIX_ROW_ENABLED_P (f->desired_matrix, last_row)) + write_row (f, last_row, updating_menu_p); - /* Now update the rest of the lines. */ - for (i = 0; i < desired_matrix->nrows - 1 && (force_p || !input_pending); i++) + bool pause_p = false; + if (first_row >= 0) { - if (MATRIX_ROW_ENABLED_P (desired_matrix, i)) - { - /* Note that output_buffer_size being 0 means that we want the - old default behavior of flushing output every now and then. */ - if (FRAME_TERMCAP_P (f) && FRAME_TTY (f)->output_buffer_size == 0) - { - /* Flush out every so many lines. - Also flush out if likely to have more than 1k buffered - otherwise. I'm told that some telnet connections get - really screwed by more than 1k output at once. */ - FILE *display_output = FRAME_TTY (f)->output; - if (display_output) - { - ptrdiff_t outq = __fpending (display_output); - if (outq > 900 - || (outq > 20 && ((i - 1) % preempt_count == 0))) - fflush (display_output); - } - } + const int preempt_count = clip_to_bounds (1, baud_rate / 2400 + 1, INT_MAX); - if (!force_p && (i - 1) % preempt_count == 0) - detect_input_pending_ignore_squeezables (); + for (int i = first_row, n = 0; i < last_row; ++i) + if (MATRIX_ROW_ENABLED_P (f->desired_matrix, i)) + { + if (!force_p && n % preempt_count == 0 + && detect_input_pending_ignore_squeezables ()) + { + pause_p = true; + break; + } - update_frame_line (f, i, updating_menu_p); - } + write_row (f, i, updating_menu_p); + ++n; + } } - pause_p = 0 < i && i < FRAME_TOTAL_LINES (f) - 1; - /* Now just clean up termcap drivers and set cursor, etc. */ if (!pause_p && set_cursor_p) - { - if ((cursor_in_echo_area - /* If we are showing a message instead of the mini-buffer, - show the cursor for the message instead of for the - (now hidden) mini-buffer contents. */ - || (BASE_EQ (minibuf_window, selected_window) - && BASE_EQ (minibuf_window, echo_area_window) - && !NILP (echo_area_buffer[0]))) - /* These cases apply only to the frame that contains - the active mini-buffer window. */ - && FRAME_HAS_MINIBUF_P (f) - && BASE_EQ (FRAME_MINIBUF_WINDOW (f), echo_area_window)) - { - int top = WINDOW_TOP_EDGE_LINE (XWINDOW (FRAME_MINIBUF_WINDOW (f))); - int col; - - /* Put cursor at the end of the prompt. If the mini-buffer - is several lines high, find the last line that has - any text on it. */ - int row = FRAME_TOTAL_LINES (f); - do - { - row--; - col = 0; - - if (MATRIX_ROW_ENABLED_P (current_matrix, row)) - { - /* Frame rows are filled up with spaces that - must be ignored here. */ - struct glyph_row *r = MATRIX_ROW (current_matrix, row); - struct glyph *start = r->glyphs[TEXT_AREA]; - - col = r->used[TEXT_AREA]; - while (0 < col && start[col - 1].charpos < 0) - col--; - } - } - while (row > top && col == 0); - - /* We exit the loop with COL at the glyph _after_ the last one. */ - if (col > 0) - col--; - - /* Make sure COL is not out of range. */ - if (col >= FRAME_CURSOR_X_LIMIT (f)) - { - /* If we have another row, advance cursor into it. */ - if (row < FRAME_TOTAL_LINES (f) - 1) - { - col = FRAME_LEFT_SCROLL_BAR_COLS (f); - row++; - } - /* Otherwise move it back in range. */ - else - col = FRAME_CURSOR_X_LIMIT (f) - 1; - } - - cursor_to (f, row, col); - } - else - { - /* We have only one cursor on terminal frames. Use it to - display the cursor of the selected window. */ - struct window *w = XWINDOW (FRAME_SELECTED_WINDOW (f)); - if (w->cursor.vpos >= 0 - /* The cursor vpos may be temporarily out of bounds - in the following situation: There is one window, - with the cursor in the lower half of it. The window - is split, and a message causes a redisplay before - a new cursor position has been computed. */ - && w->cursor.vpos < WINDOW_TOTAL_LINES (w)) - { - int x = WINDOW_TO_FRAME_HPOS (w, w->cursor.hpos); - int y = WINDOW_TO_FRAME_VPOS (w, w->cursor.vpos); + tty_set_cursor (); - x += max (0, w->left_margin_cols); - cursor_to (f, y, x); - } - } - } - - do_pause: - - clear_desired_matrices (f); return pause_p; } - /* Do line insertions/deletions on frame F for frame-based redisplay. */ static bool @@ -5209,12 +5865,12 @@ scrolling (struct frame *frame) which is LEN glyphs long. */ static int -count_blanks (struct glyph *r, int len) +count_blanks (struct frame *f, struct glyph *r, int len) { int i; for (i = 0; i < len; ++i) - if (!CHAR_GLYPH_SPACE_P (r[i])) + if (!CHAR_GLYPH_SPACE_P (f, r[i])) break; return i; @@ -5250,7 +5906,7 @@ #define char_ins_del_cost(f) (&char_ins_del_vector[FRAME_TOTAL_COLS (f)]) /* Perform a frame-based update on line VPOS in frame FRAME. */ static void -update_frame_line (struct frame *f, int vpos, bool updating_menu_p) +write_row (struct frame *f, int vpos, bool updating_menu_p) { struct glyph *obody, *nbody, *op1, *op2, *np1, *nend; int tem; @@ -5264,11 +5920,6 @@ update_frame_line (struct frame *f, int vpos, bool updating_menu_p) bool colored_spaces_p = (FACE_FROM_ID (f, DEFAULT_FACE_ID)->background != FACE_TTY_DEFAULT_BG_COLOR); - /* This should never happen, but evidently sometimes does if one - resizes the frame quickly enough. Prevent aborts in cmcheckmagic. */ - if (vpos >= FRAME_TOTAL_LINES (f)) - return; - if (colored_spaces_p) write_spaces_p = 1; @@ -5287,7 +5938,7 @@ update_frame_line (struct frame *f, int vpos, bool updating_menu_p) /* Ignore trailing spaces, if we can. */ if (!write_spaces_p) - while (olen > 0 && CHAR_GLYPH_SPACE_P (obody[olen-1])) + while (olen > 0 && CHAR_GLYPH_SPACE_P (f, obody[olen-1])) olen--; } @@ -5316,7 +5967,7 @@ update_frame_line (struct frame *f, int vpos, bool updating_menu_p) { /* Ignore spaces at the end, if we can. */ if (!write_spaces_p) - while (nlen > 0 && CHAR_GLYPH_SPACE_P (nbody[nlen - 1])) + while (nlen > 0 && CHAR_GLYPH_SPACE_P (f, nbody[nlen - 1])) --nlen; /* Write the contents of the desired line. */ @@ -5338,15 +5989,13 @@ update_frame_line (struct frame *f, int vpos, bool updating_menu_p) /* Make sure we are in the right row, otherwise cursor movement with cmgoto might use `ch' in the wrong row. */ cursor_to (f, vpos, 0); - - make_current (desired_matrix, current_matrix, vpos); return; } /* Pretend trailing spaces are not there at all, unless for one reason or another we must write all spaces. */ if (!write_spaces_p) - while (nlen > 0 && CHAR_GLYPH_SPACE_P (nbody[nlen - 1])) + while (nlen > 0 && CHAR_GLYPH_SPACE_P (f, nbody[nlen - 1])) nlen--; /* If there's no i/d char, quickly do the best we can without it. */ @@ -5383,9 +6032,6 @@ update_frame_line (struct frame *f, int vpos, bool updating_menu_p) cursor_to (f, vpos, nlen); clear_end_of_line (f, olen); } - - /* Make current row = desired row. */ - make_current (desired_matrix, current_matrix, vpos); return; } @@ -5399,7 +6045,7 @@ update_frame_line (struct frame *f, int vpos, bool updating_menu_p) if (write_spaces_p) nsp = 0; else - nsp = count_blanks (nbody, nlen); + nsp = count_blanks (f, nbody, nlen); if (nlen > nsp) { @@ -5407,14 +6053,12 @@ update_frame_line (struct frame *f, int vpos, bool updating_menu_p) write_glyphs (f, nbody + nsp, nlen - nsp); } - /* Exchange contents between current_frame and new_frame. */ - make_current (desired_matrix, current_matrix, vpos); return; } /* Compute number of leading blanks in old and new contents. */ - osp = count_blanks (obody, olen); - nsp = (colored_spaces_p ? 0 : count_blanks (nbody, nlen)); + osp = count_blanks (f, obody, olen); + nsp = (colored_spaces_p ? 0 : count_blanks (f, nbody, nlen)); /* Compute number of matching chars starting with first non-blank. */ begmatch = count_match (obody + osp, obody + olen, @@ -5425,7 +6069,7 @@ update_frame_line (struct frame *f, int vpos, bool updating_menu_p) if (!write_spaces_p && osp + begmatch == olen) { np1 = nbody + nsp; - while (np1 + begmatch < nend && CHAR_GLYPH_SPACE_P (np1[begmatch])) + while (np1 + begmatch < nend && CHAR_GLYPH_SPACE_P (f, np1[begmatch])) ++begmatch; } @@ -5566,9 +6210,6 @@ update_frame_line (struct frame *f, int vpos, bool updating_menu_p) cursor_to (f, vpos, nlen); clear_end_of_line (f, olen); } - - /* Exchange contents between current_frame and new_frame. */ - make_current (desired_matrix, current_matrix, vpos); } @@ -5967,7 +6608,8 @@ handle_window_change_signal (int sig) { struct frame *f = XFRAME (frame); - if (FRAME_TERMCAP_P (f) && FRAME_TTY (f) == tty) + if (FRAME_TERMCAP_P (f) && FRAME_TTY (f) == tty + && !FRAME_PARENT_FRAME (f)) /* Record the new sizes, but don't reallocate the data structures now. Let that be done later outside of the signal handler. */ @@ -6534,7 +7176,7 @@ init_display_interactive (void) /* Construct the space glyph. */ space_glyph.type = CHAR_GLYPH; - SET_CHAR_GLYPH (space_glyph, ' ', DEFAULT_FACE_ID, 0); + SET_CHAR_GLYPH (NULL, space_glyph, ' ', DEFAULT_FACE_ID, 0); space_glyph.charpos = -1; inverse_video = 0; @@ -6808,6 +7450,7 @@ syms_of_display (void) defsubr (&Ssend_string_to_terminal); defsubr (&Sinternal_show_cursor); defsubr (&Sinternal_show_cursor_p); + defsubr (&Sframe__z_order_lessp); #ifdef GLYPH_DEBUG defsubr (&Sdump_redisplay_history); @@ -6818,8 +7461,10 @@ syms_of_display (void) /* This is the "purpose" slot of a display table. */ DEFSYM (Qdisplay_table, "display-table"); + DEFSYM (Qframe__z_order_lessp, "frame--z-order-lessp"); DEFSYM (Qredisplay_dont_pause, "redisplay-dont-pause"); + DEFSYM (Qtty_non_selected_cursor, "tty-non-selected-cursor"); DEFVAR_INT ("baud-rate", baud_rate, doc: /* The output baud rate of the terminal. @@ -6921,6 +7566,8 @@ syms_of_display (void) This option affects only builds where the tool bar is not external. */); pdumper_do_now_and_after_load (syms_of_display_for_pdumper); + + Fprovide (intern_c_string ("tty-child-frames"), Qnil); } static void modified src/disptab.h @@ -28,7 +28,7 @@ #define DISP_TABLE_P(obj) \ && EQ (XCHAR_TABLE (obj)->purpose, Qdisplay_table) \ && CHAR_TABLE_EXTRA_SLOTS (XCHAR_TABLE (obj)) == DISP_TABLE_EXTRA_SLOTS) -#define DISP_TABLE_EXTRA_SLOTS 6 +#define DISP_TABLE_EXTRA_SLOTS 12 #define DISP_TRUNC_GLYPH(dp) ((dp)->extras[0]) #define DISP_CONTINUE_GLYPH(dp) ((dp)->extras[1]) #define DISP_ESCAPE_GLYPH(dp) ((dp)->extras[2]) @@ -36,6 +36,16 @@ #define DISP_CTRL_GLYPH(dp) ((dp)->extras[3]) #define DISP_INVIS_VECTOR(dp) ((dp)->extras[4]) #define DISP_BORDER_GLYPH(dp) ((dp)->extras[5]) +enum box +{ + BOX_VERTICAL = 6, + BOX_HORIZONTAL, + BOX_DOWN_RIGHT, + BOX_DOWN_LEFT, + BOX_UP_RIGHT, + BOX_UP_LEFT +}; + extern Lisp_Object disp_char_vector (struct Lisp_Char_Table *, int); #define DISP_CHAR_VECTOR(dp, c) \ modified src/frame.c @@ -130,6 +130,14 @@ decode_window_system_frame (Lisp_Object frame) #endif } +struct frame * +decode_tty_frame (Lisp_Object frame) +{ + struct frame *f = decode_live_frame (frame); + check_tty (f); + return f; +} + void check_window_system (struct frame *f) { @@ -141,6 +149,13 @@ check_window_system (struct frame *f) : "Window system is not in use or not initialized"); } +void +check_tty (struct frame *f) +{ + if (!f || !FRAME_TERMCAP_P (f)) + error ("tty frame should be used"); +} + /* Return the value of frame parameter PROP in frame FRAME. */ Lisp_Object @@ -182,6 +197,17 @@ set_menu_bar_lines (struct frame *f, Lisp_Object value, Lisp_Object oldval) int olines = FRAME_MENU_BAR_LINES (f); int nlines = TYPE_RANGED_FIXNUMP (int, value) ? XFIXNUM (value) : 0; + /* Menu bars on child frames don't work on all platforms, which is + the reason why prepare_menu_bar does not update_menu_bar for + child frames (info from Martin Rudalics). This could be + implemented in ttys, but it's probaly not worth it. */ + if (is_tty_child_frame (f)) + { + FRAME_MENU_BAR_LINES (f) = 0; + FRAME_MENU_BAR_HEIGHT (f) = 0; + return; + } + /* Right now, menu bars don't work properly in minibuf-only frames; most of the commands try to apply themselves to the minibuffer frame itself, and get an error because you can't switch buffers @@ -370,17 +396,17 @@ frame_windows_min_size (Lisp_Object frame, Lisp_Object horizontal, } else retval = XFIXNUM (call4 (Qframe_windows_min_size, frame, horizontal, - ignore, pixelwise)); + ignore, pixelwise)); /* Don't allow too small height of text-mode frames, or else cm.c might abort in cmcheckmagic. */ if ((FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) && NILP (horizontal)) { - int min_height = (FRAME_MENU_BAR_LINES (f) - + FRAME_TAB_BAR_LINES (f) + int min_height = (FRAME_MENU_BAR_LINES (f) + FRAME_TAB_BAR_LINES (f) + FRAME_WANTS_MODELINE_P (f) - + 2); /* one text line and one echo-area line */ - + + FRAME_HAS_MINIBUF_P (f)); + if (min_height == 0) + min_height = 1; if (retval < min_height) retval = min_height; } @@ -389,7 +415,6 @@ frame_windows_min_size (Lisp_Object frame, Lisp_Object horizontal, } -#ifdef HAVE_WINDOW_SYSTEM /** * keep_ratio: * @@ -508,7 +533,6 @@ keep_ratio (struct frame *f, struct frame *p, int old_width, int old_height, } } } -#endif static void @@ -833,8 +857,9 @@ adjust_frame_size (struct frame *f, int new_text_width, int new_text_height, /* MSDOS frames cannot PRETEND, as they change frame size by manipulating video hardware. */ - if ((FRAME_TERMCAP_P (f) && !pretend) || FRAME_MSDOS_P (f)) - FrameCols (FRAME_TTY (f)) = new_text_cols; + if (is_tty_root_frame (f)) + if ((FRAME_TERMCAP_P (f) && !pretend) || FRAME_MSDOS_P (f)) + FrameCols (FRAME_TTY (f)) = new_text_cols; #if defined (HAVE_WINDOW_SYSTEM) if (WINDOWP (f->tab_bar_window)) @@ -866,9 +891,10 @@ adjust_frame_size (struct frame *f, int new_text_width, int new_text_height, resize_frame_windows (f, new_inner_height, false); /* MSDOS frames cannot PRETEND, as they change frame size by - manipulating video hardware. */ - if ((FRAME_TERMCAP_P (f) && !pretend) || FRAME_MSDOS_P (f)) - FrameRows (FRAME_TTY (f)) = new_text_lines + FRAME_TOP_MARGIN (f); + manipulating video hardware. */ + if (is_tty_root_frame (f)) + if ((FRAME_TERMCAP_P (f) && !pretend) || FRAME_MSDOS_P (f)) + FrameRows (FRAME_TTY (f)) = new_text_lines + FRAME_TOP_MARGIN (f); } else if (new_text_lines != old_text_lines) call2 (Qwindow__pixel_to_total, frame, Qnil); @@ -898,6 +924,9 @@ adjust_frame_size (struct frame *f, int new_text_width, int new_text_height, adjust_frame_glyphs (f); calculate_costs (f); SET_FRAME_GARBAGED (f); + if (is_tty_child_frame (f)) + SET_FRAME_GARBAGED (root_frame (f)); + /* We now say here that F was resized instead of using the old condition below. Some resizing must have taken place and if it was only shifting the root window's position (paranoia?). */ @@ -910,7 +939,6 @@ adjust_frame_size (struct frame *f, int new_text_width, int new_text_height, unblock_input (); -#ifdef HAVE_WINDOW_SYSTEM { /* Adjust size of F's child frames. */ Lisp_Object frames, frame1; @@ -920,7 +948,6 @@ adjust_frame_size (struct frame *f, int new_text_width, int new_text_height, keep_ratio (XFRAME (frame1), f, old_native_width, old_native_height, new_native_width, new_native_height); } -#endif } /* Allocate basically initialized frame. */ @@ -967,12 +994,12 @@ make_frame (bool mini_p) f->line_height = 1; /* !FRAME_WINDOW_P value. */ f->new_width = -1; f->new_height = -1; + f->no_special_glyphs = false; #ifdef HAVE_WINDOW_SYSTEM f->vertical_scroll_bar_type = vertical_scroll_bar_none; f->horizontal_scroll_bars = false; f->want_fullscreen = FULLSCREEN_NONE; f->undecorated = false; - f->no_special_glyphs = false; #ifndef HAVE_NTGUI f->override_redirect = false; #endif @@ -1089,7 +1116,6 @@ make_frame (bool mini_p) return f; } -#ifdef HAVE_WINDOW_SYSTEM /* Make a frame using a separate minibuffer window on another frame. MINI_WINDOW is the minibuffer window to use. nil means use the default (the global minibuffer). */ @@ -1183,7 +1209,7 @@ make_minibuffer_frame (void) : Fcar (Vminibuffer_list)), 0, 0); return f; } -#endif /* HAVE_WINDOW_SYSTEM */ + /* Construct a frame that refers to a terminal. */ @@ -1209,7 +1235,7 @@ make_initial_frame (void) tty_frame_count = 1; fset_name (f, build_pure_c_string ("F1")); - SET_FRAME_VISIBLE (f, 1); + SET_FRAME_VISIBLE (f, true); f->output_method = terminal->type; f->terminal = terminal; @@ -1246,23 +1272,48 @@ make_initial_frame (void) #ifndef HAVE_ANDROID static struct frame * -make_terminal_frame (struct terminal *terminal) +make_terminal_frame (struct terminal *terminal, Lisp_Object parent, + Lisp_Object params) { - register struct frame *f; - Lisp_Object frame; char name[sizeof "F" + INT_STRLEN_BOUND (tty_frame_count)]; if (!terminal->name) error ("Terminal is not live, can't create new frames on it"); - f = make_frame (1); + struct frame *f; + if (NILP (parent)) + f = make_frame (true); + else + { + CHECK_LIVE_FRAME (parent); + + f = NULL; + Lisp_Object mini = Fassq (Qminibuffer, params); + if (CONSP (mini)) + { + mini = Fcdr (mini); + struct kboard *kb = FRAME_KBOARD (XFRAME (parent)); + if (EQ (mini, Qnone) || NILP (mini)) + f = make_frame_without_minibuffer (Qnil, kb, Qnil); + else if (EQ (mini, Qonly)) + error ("minibuffer-only child frames are not implemented"); + else if (WINDOWP (mini)) + f = make_frame_without_minibuffer (mini, kb, Qnil); + } + if (f == NULL) + f = make_frame (true); + f->parent_frame = parent; + f->z_order = 1 + max_child_z_order (XFRAME (parent)); + } + + Lisp_Object frame; XSETFRAME (frame, f); Vframe_list = Fcons (frame, Vframe_list); fset_name (f, make_formatted_string (name, "F%"PRIdMAX, ++tty_frame_count)); - SET_FRAME_VISIBLE (f, 1); + SET_FRAME_VISIBLE (f, true); f->terminal = terminal; f->terminal->reference_count++; @@ -1287,7 +1338,15 @@ make_terminal_frame (struct terminal *terminal) f->horizontal_scroll_bars = false; #endif - FRAME_MENU_BAR_LINES (f) = NILP (Vmenu_bar_mode) ? 0 : 1; + /* Menu bars on child frames don't work on all platforms, which is + the reason why prepare_menu_bar does not update_menu_bar for + child frames (info from Martin Rudalics). This could be + implemented in ttys, but it's unclear if it is worth it. */ + if (NILP (parent)) + FRAME_MENU_BAR_LINES (f) = NILP (Vmenu_bar_mode) ? 0 : 1; + else + FRAME_MENU_BAR_LINES (f) = 0; + FRAME_TAB_BAR_LINES (f) = NILP (Vtab_bar_mode) ? 0 : 1; FRAME_LINES (f) = FRAME_LINES (f) - FRAME_MENU_BAR_LINES (f) - FRAME_TAB_BAR_LINES (f); @@ -1296,16 +1355,18 @@ make_terminal_frame (struct terminal *terminal) FRAME_TEXT_HEIGHT (f) = FRAME_TEXT_HEIGHT (f) - FRAME_MENU_BAR_HEIGHT (f) - FRAME_TAB_BAR_HEIGHT (f); - /* Set the top frame to the newly created frame. */ - if (FRAMEP (FRAME_TTY (f)->top_frame) - && FRAME_LIVE_P (XFRAME (FRAME_TTY (f)->top_frame))) - SET_FRAME_VISIBLE (XFRAME (FRAME_TTY (f)->top_frame), 2); /* obscured */ + /* Mark current topmost frame obscured if we make a new root frame. + Child frames don't completely obscure other frames. */ + if (NILP (parent) && FRAMEP (FRAME_TTY (f)->top_frame)) + { + struct frame *top = XFRAME (FRAME_TTY (f)->top_frame); + struct frame *root = root_frame (top); + if (FRAME_LIVE_P (root)) + SET_FRAME_VISIBLE (root, false); + } + /* Set the top frame to the newly created frame. */ FRAME_TTY (f)->top_frame = frame; - - if (!noninteractive) - init_frame_faces (f); - return f; } @@ -1335,6 +1396,68 @@ get_future_frame_param (Lisp_Object parameter, #endif +static int +tty_child_pos_param (struct frame *child, Lisp_Object key, + Lisp_Object params, int dflt) +{ + Lisp_Object val = Fassq (key, params); + if (CONSP (val)) + { + val = XCDR (val); + if (FIXNUMP (val)) + return XFIXNUM (val); + } + return dflt; +} + +static int +tty_child_size_param (struct frame *child, Lisp_Object key, + Lisp_Object params, int dflt) +{ + Lisp_Object val = Fassq (key, params); + if (CONSP (val)) + { + val = XCDR (val); + if (CONSP (val)) + { + /* Width and height may look like (width text-pixels . PIXELS) + on window systems. Mimic that. */ + val = XCDR (val); + if (EQ (val, Qtext_pixels)) + val = XCDR (val); + } + else if (FLOATP (val)) + { + /* Width and height may be a float, in which case + it's a multiple of the parent's value. */ + struct frame *parent = FRAME_PARENT_FRAME (child); + eassert (parent); /* the caller ensures this, but... */ + if (parent) + { + int sz = (EQ (key, Qwidth) ? FRAME_TOTAL_COLS (parent) + : FRAME_TOTAL_LINES (parent)); + val = make_fixnum (XFLOAT_DATA (val) * sz); + } + else + val = Qnil; + } + + if (FIXNATP (val)) + return XFIXNUM (val); + } + return dflt; +} + +static void +tty_child_frame_rect (struct frame *f, Lisp_Object params, + int *x, int *y, int *w, int *h) +{ + *x = tty_child_pos_param (f, Qleft, params, 0); + *y = tty_child_pos_param (f, Qtop, params, 0); + *w = tty_child_size_param (f, Qwidth, params, FRAME_TOTAL_COLS (f)); + *h = tty_child_size_param (f, Qheight, params, FRAME_TOTAL_LINES (f)); +} + DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame, 1, 1, 0, doc: /* Create an additional terminal frame, possibly on another terminal. @@ -1358,9 +1481,7 @@ DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame, error ("Text terminals are not supported on this platform"); return Qnil; #else - struct frame *f; struct terminal *t = NULL; - Lisp_Object frame; struct frame *sf = SELECTED_FRAME (); #ifdef MSDOS @@ -1390,7 +1511,7 @@ DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame, error ("Multiple terminals are not supported on this platform"); if (!t) t = the_only_display_info.terminal; -#endif +# endif } if (!t) @@ -1417,19 +1538,64 @@ DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame, SAFE_FREE (); } - f = make_terminal_frame (t); + /* Make a new frame. We need to know up front if a parent frame is + specified because we behave differently in this case, e.g., child + frames don't obscure other frames. */ + Lisp_Object parent = Fcdr (Fassq (Qparent_frame, parms)); + struct frame *f = make_terminal_frame (t, parent, parms); - { - int width, height; - get_tty_size (fileno (FRAME_TTY (f)->input), &width, &height); - /* With INHIBIT 5 pass correct text height to adjust_frame_size. */ - adjust_frame_size (f, width, height - FRAME_TOP_MARGIN (f), - 5, 0, Qterminal_frame); - } + if (!noninteractive) + init_frame_faces (f); + + /* Visibility of root frames cannot be set with a frame parameter. + Their visibility solely depends on whether or not they are the + top_frame on the terminal. */ + if (FRAME_PARENT_FRAME (f)) + { + Lisp_Object visible = Fassq (Qvisibility, parms); + if (CONSP (visible)) + SET_FRAME_VISIBLE (f, !NILP (visible)); + + /* FIXME/tty: The only way, for now, to get borders on a tty is + to allow decorations. */ + Lisp_Object undecorated = Fassq (Qundecorated, parms); + if (CONSP (undecorated) && !NILP (XCDR (undecorated))) + f->undecorated = true; + + /* Unused at present. */ + Lisp_Object no_focus = Fassq (Qno_accept_focus, parms); + if (CONSP (no_focus) && !NILP (XCDR (no_focus))) + f->no_accept_focus = true; + + Lisp_Object no_split = Fassq (Qunsplittable, parms); + if (CONSP (no_split) && !NILP (XCDR (no_split))) + f->no_split = true; + } + /* Determine width and height of the frame. For root frames use the + width/height of the terminal. For child frames, take it from frame + parameters. Note that a default (80x25) has been set in + make_frame. We handle root frames in this way because otherwise we + would end up needing glyph matrices for the terminal, which is both + more work and has its downsides (think of clipping frames to the + terminal size). */ + int x = 0, y = 0, width, height; + if (FRAME_PARENT_FRAME (f)) + tty_child_frame_rect (f, parms, &x, &y, &width, &height); + else + get_tty_size (fileno (FRAME_TTY (f)->input), &width, &height); + adjust_frame_size (f, width, height - FRAME_TOP_MARGIN (f), 5, 0, + Qterminal_frame); adjust_frame_glyphs (f); + calculate_costs (f); - XSETFRAME (frame, f); + + f->left_pos = x; + f->top_pos = y; + store_in_alist (&parms, Qleft, make_fixnum (x)); + store_in_alist (&parms, Qtop, make_fixnum (y)); + store_in_alist (&parms, Qwidth, make_fixnum (width)); + store_in_alist (&parms, Qheight, make_fixnum (height)); store_in_alist (&parms, Qtty_type, build_string (t->display_info.tty->type)); store_in_alist (&parms, Qtty, @@ -1451,7 +1617,11 @@ DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame, /* On terminal frames the `minibuffer' frame parameter is always virtually t. Avoid that a different value in parms causes complaints, see Bug#24758. */ - store_in_alist (&parms, Qminibuffer, Qt); + if (!FRAME_PARENT_FRAME (f)) + store_in_alist (&parms, Qminibuffer, Qt); + + Lisp_Object frame; + XSETFRAME (frame, f); Fmodify_frame_parameters (frame, parms); f->can_set_window_size = true; @@ -1480,8 +1650,6 @@ DEFUN ("make-terminal-frame", Fmake_terminal_frame, Smake_terminal_frame, Lisp_Object do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object norecord) { - struct frame *sf = SELECTED_FRAME (), *f; - /* If FRAME is a switch-frame event, extract the frame we should switch to. */ if (CONSP (frame) @@ -1493,7 +1661,9 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor a switch-frame event to arrive after a frame is no longer live, especially when deleting the initial frame during startup. */ CHECK_FRAME (frame); - f = XFRAME (frame); + struct frame *f = XFRAME (frame); + struct frame *sf = SELECTED_FRAME (); + /* Silently ignore dead and tooltip frames (Bug#47207). */ if (!FRAME_LIVE_P (f) || FRAME_TOOLTIP_P (f)) return Qnil; @@ -1546,24 +1716,37 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor struct tty_display_info *tty = FRAME_TTY (f); Lisp_Object top_frame = tty->top_frame; - /* Don't mark the frame garbaged and/or obscured if we are - switching to the frame that is already the top frame of that - TTY. */ + /* Don't mark the frame garbaged if we are switching to the frame + that is already the top frame of that TTY. */ if (!EQ (frame, top_frame)) { + struct frame *new_root = root_frame (f); + SET_FRAME_VISIBLE (new_root, true); + SET_FRAME_VISIBLE (f, true); + + /* Mark previously displayed frame as no longer visible. */ if (FRAMEP (top_frame)) - /* Mark previously displayed frame as now obscured. */ - SET_FRAME_VISIBLE (XFRAME (top_frame), 2); - SET_FRAME_VISIBLE (f, 1); - /* If the new TTY frame changed dimensions, we need to - resync term.c's idea of the frame size with the new - frame's data. */ - if (FRAME_COLS (f) != FrameCols (tty)) - FrameCols (tty) = FRAME_COLS (f); - if (FRAME_TOTAL_LINES (f) != FrameRows (tty)) - FrameRows (tty) = FRAME_TOTAL_LINES (f); + { + struct frame *top = XFRAME (top_frame); + struct frame *old_root = root_frame (top); + if (old_root != new_root) + SET_FRAME_VISIBLE (old_root, false); + } + + tty->top_frame = frame; + + /* FIXME: Why is it correct to set FrameCols/Rows? */ + if (!FRAME_PARENT_FRAME (f)) + { + /* If the new TTY frame changed dimensions, we need to + resync term.c's idea of the frame size with the new + frame's data. */ + if (FRAME_COLS (f) != FrameCols (tty)) + FrameCols (tty) = FRAME_COLS (f); + if (FRAME_TOTAL_LINES (f) != FrameRows (tty)) + FrameRows (tty) = FRAME_TOTAL_LINES (f); + } } - tty->top_frame = frame; } sf->select_mini_window_flag = MINI_WINDOW_P (XWINDOW (sf->selected_window)); @@ -1605,10 +1788,36 @@ do_switch_frame (Lisp_Object frame, int track, int for_deletion, Lisp_Object nor (select-window (frame-root-window (make-frame))) doesn't end up with your typing being interpreted in the new frame instead of the one you're actually typing in. */ -#ifdef HAVE_WINDOW_SYSTEM - if (!frame_ancestor_p (f, sf)) -#endif - internal_last_event_frame = Qnil; + + /* FIXME/tty: I don't understand this. (The comment above is from + Jim BLandy 1993 BTW, and the frame_ancestor_p from 2017.) + + Setting the last event frame to nil leads to switch-frame events + being generated even if they normally wouldn't be because the frame + in question equals selected-frame. See the places in keyboard.c + where make_lispy_switch_frame is called. + + This leads to problems at least on ttys. + + Imagine that we have functions in post-command-hook that use + select-frame in some way (e.g., with-selected-window). Let these + functions select different frames during the execution of + post-command-hook in command_loop_1. Setting + internal_last_event_frame to nil here makes these select-frame + calls (potentially and in reality) generate switch-frame events. + (But only in one direction (frame_ancestor_p), which I also don't + understand). + + These switch-frame events form an endless loop in + command_loop_1. It runs post-command-hook, which generates + switch-frame events, which command_loop_1 finds (bound to '#ignore) + and executes, which again runs post-command-hook etc., ad + infinitum. + + Let's not do that for now on ttys. */ + if (!is_tty_frame (f)) + if (!frame_ancestor_p (f, sf)) + internal_last_event_frame = Qnil; return frame; } @@ -1725,7 +1934,6 @@ DEFUN ("frame-parent", Fframe_parent, Sframe_parent, return Qnil; } -#ifdef HAVE_WINDOW_SYSTEM bool frame_ancestor_p (struct frame *af, struct frame *df) { @@ -1741,7 +1949,6 @@ frame_ancestor_p (struct frame *af, struct frame *df) return false; } -#endif DEFUN ("frame-ancestor-p", Fframe_ancestor_p, Sframe_ancestor_p, 2, 2, 0, @@ -1752,15 +1959,10 @@ DEFUN ("frame-ancestor-p", Fframe_ancestor_p, Sframe_ancestor_p, frame. */) (Lisp_Object ancestor, Lisp_Object descendant) { -#ifdef HAVE_WINDOW_SYSTEM struct frame *af = decode_live_frame (ancestor); struct frame *df = decode_live_frame (descendant); - return frame_ancestor_p (af, df) ? Qt : Qnil; -#else - return Qnil; -#endif - } +} /* Return CANDIDATE if it can be used as 'other-than-FRAME' frame on the same tty (for tty frames) or among frames which uses FRAME's keyboard. @@ -2021,7 +2223,9 @@ other_frames (struct frame *f, bool invisible, bool force) && (invisible || NILP (get_frame_param (f1, Qdelete_before))) /* For invisibility and normal deletions, at least one visible or iconified frame must remain (Bug#26682). */ - && (FRAME_VISIBLE_P (f1) || FRAME_ICONIFIED_P (f1) + && (FRAME_VISIBLE_P (f1) + || is_tty_frame (f1) + || FRAME_ICONIFIED_P (f1) || (!invisible && (force /* Allow deleting the terminal frame when at @@ -2282,7 +2486,7 @@ delete_frame (Lisp_Object frame, Lisp_Object force) fset_root_window (f, Qnil); Vframe_list = Fdelq (frame, Vframe_list); - SET_FRAME_VISIBLE (f, 0); + SET_FRAME_VISIBLE (f, false); /* Allow the vector of menu bar contents to be freed in the next garbage collection. The frame object itself may not be garbage @@ -2868,6 +3072,12 @@ DEFUN ("make-frame-visible", Fmake_frame_visible, Smake_frame_visible, if (FRAME_WINDOW_P (f) && FRAME_TERMINAL (f)->frame_visible_invisible_hook) FRAME_TERMINAL (f)->frame_visible_invisible_hook (f, true); + if (is_tty_frame (f)) + { + SET_FRAME_VISIBLE (f, true); + tty_raise_lower_frame (f, true); + } + make_frame_visible_1 (f->root_window); /* Make menu bar update for the Buffers and Frames menus. */ @@ -2918,6 +3128,12 @@ DEFUN ("make-frame-invisible", Fmake_frame_invisible, Smake_frame_invisible, if (FRAME_WINDOW_P (f) && FRAME_TERMINAL (f)->frame_visible_invisible_hook) FRAME_TERMINAL (f)->frame_visible_invisible_hook (f, false); + /* The ELisp manual says that this "usually" makes child frames + invisible, too, but without saying when not. Since users can't + rely on this, it's not implemented. */ + if (is_tty_frame (f)) + SET_FRAME_VISIBLE (f, false); + /* Make menu bar update for the Buffers and Frames menus. */ windows_or_buffers_changed = 16; @@ -2977,10 +3193,13 @@ DEFUN ("frame-visible-p", Fframe_visible_p, Sframe_visible_p, (Lisp_Object frame) { CHECK_LIVE_FRAME (frame); + struct frame *f = XFRAME (frame); - if (FRAME_VISIBLE_P (XFRAME (frame))) + if (FRAME_VISIBLE_P (f)) + return Qt; + else if (is_tty_root_frame (f)) return Qt; - if (FRAME_ICONIFIED_P (XFRAME (frame))) + if (FRAME_ICONIFIED_P (f)) return Qicon; return Qnil; } @@ -3012,12 +3231,7 @@ DEFUN ("raise-frame", Fraise_frame, Sraise_frame, 0, 1, "", XSETFRAME (frame, f); - if (FRAME_TERMCAP_P (f)) - /* On a text terminal select FRAME. */ - Fselect_frame (frame, Qnil); - else - /* Do like the documentation says. */ - Fmake_frame_visible (frame); + Fmake_frame_visible (frame); if (FRAME_TERMINAL (f)->frame_raise_lower_hook) (*FRAME_TERMINAL (f)->frame_raise_lower_hook) (f, true); @@ -3318,6 +3532,15 @@ store_frame_param (struct frame *f, Lisp_Object prop, Lisp_Object val) val = old_val; } + /* Re-parenting is currently not implemented when changing a root + frame to a child frame or vice versa. */ + if (is_tty_frame (f) && EQ (prop, Qparent_frame)) + { + if (NILP (f->parent_frame) != NILP (val)) + error ("Making a root frame a child or vice versa is not supported"); + f->parent_frame = val; + } + /* The tty color needed to be set before the frame's parameter alist was updated with the new value. This is not true any more, but we still do this test early on. */ @@ -3441,13 +3664,10 @@ DEFUN ("frame-parameters", Fframe_parameters, Sframe_parameters, 0, 1, 0, else #endif { - /* This ought to be correct in f->param_alist for an X frame. */ - Lisp_Object lines; - - XSETFASTINT (lines, FRAME_MENU_BAR_LINES (f)); - store_in_alist (&alist, Qmenu_bar_lines, lines); - XSETFASTINT (lines, FRAME_TAB_BAR_LINES (f)); - store_in_alist (&alist, Qtab_bar_lines, lines); + store_in_alist (&alist, Qmenu_bar_lines, make_fixnum (FRAME_MENU_BAR_LINES (f))); + store_in_alist (&alist, Qtab_bar_lines, make_fixnum (FRAME_TAB_BAR_LINES (f))); + store_in_alist (&alist, Qvisibility, FRAME_VISIBLE_P (f) ? Qt : Qnil); + store_in_alist (&alist, Qno_accept_focus, FRAME_NO_ACCEPT_FOCUS (f) ? Qt : Qnil); } return alist; @@ -3525,7 +3745,6 @@ DEFUN ("frame-parameter", Fframe_parameter, Sframe_parameter, 2, 2, 0, return value; } - DEFUN ("modify-frame-parameters", Fmodify_frame_parameters, Smodify_frame_parameters, 2, 2, 0, doc: /* Modify FRAME according to new values of its parameters in ALIST. @@ -3563,6 +3782,7 @@ DEFUN ("modify-frame-parameters", Fmodify_frame_parameters, USE_SAFE_ALLOCA; SAFE_ALLOCA_LISP (parms, 2 * length); values = parms + length; + Lisp_Object params = alist; /* Extract parm names and values into those vectors. */ @@ -3588,6 +3808,31 @@ DEFUN ("modify-frame-parameters", Fmodify_frame_parameters, update_face_from_frame_parameter (f, prop, val); } + if (is_tty_child_frame (f)) + { + int x = tty_child_pos_param (f, Qleft, params, f->left_pos); + int y = tty_child_pos_param (f, Qtop, params, f->top_pos); + if (x != f->left_pos || y != f->top_pos) + { + f->left_pos = x; + f->top_pos = y; + SET_FRAME_GARBAGED (root_frame (f)); + } + + int w = tty_child_size_param (f, Qwidth, params, f->total_cols); + int h = tty_child_size_param (f, Qheight, params, f->total_lines); + if (w != f->total_cols || h != f->total_lines) + change_frame_size (f, w, h, false, false, false); + + Lisp_Object visible = Fassq (Qvisibility, params); + if (CONSP (visible)) + SET_FRAME_VISIBLE (f, !NILP (Fcdr (visible))); + + Lisp_Object no_special = Fassq (Qno_special_glyphs, params); + if (CONSP (no_special)) + FRAME_NO_SPECIAL_GLYPHS (f) = !NILP (Fcdr (no_special)); + } + SAFE_FREE (); } return Qnil; @@ -3935,6 +4180,11 @@ DEFUN ("set-frame-position", Fset_frame_position, (void) yval; #endif } + else if (is_tty_child_frame (f)) + { + f->left_pos = xval; + f->top_pos = yval; + } return Qt; } @@ -3992,9 +4242,9 @@ DEFUN ("frame-scale-factor", Fframe_scale_factor, Sframe_scale_factor, /* Connect the frame-parameter names for frames to the ways of passing the parameter values to the window system. - The name of a parameter, as a Lisp symbol, has a - `frame-parameter-pos' property which is an integer in Lisp that is - an index in this table. */ + The name of a parameter, a Lisp symbol, has an `x-frame-parameter' + property which is its index in this table. This is initialized in + syms_of_frame. */ struct frame_parm_table { const char *name; @@ -4005,13 +4255,13 @@ DEFUN ("frame-scale-factor", Fframe_scale_factor, Sframe_scale_factor, { {"auto-raise", SYMBOL_INDEX (Qauto_raise)}, {"auto-lower", SYMBOL_INDEX (Qauto_lower)}, - {"background-color", -1}, + {"background-color", SYMBOL_INDEX (Qbackground_color)}, {"border-color", SYMBOL_INDEX (Qborder_color)}, {"border-width", SYMBOL_INDEX (Qborder_width)}, {"cursor-color", SYMBOL_INDEX (Qcursor_color)}, {"cursor-type", SYMBOL_INDEX (Qcursor_type)}, - {"font", -1}, - {"foreground-color", -1}, + {"font", SYMBOL_INDEX (Qfont)}, + {"foreground-color", SYMBOL_INDEX (Qforeground_color)}, {"icon-name", SYMBOL_INDEX (Qicon_name)}, {"icon-type", SYMBOL_INDEX (Qicon_type)}, {"child-frame-border-width", SYMBOL_INDEX (Qchild_frame_border_width)}, @@ -4246,6 +4496,29 @@ frame_float (struct frame *f, Lisp_Object val, enum frame_float_type what, } } +/* Handle frame parameter change with frame parameter handler. + F is the frame whose frame parameter was changed. + PROP is the name of the frame parameter. + VAL and OLD_VALUE are the current and the old value of the + frame parameter. */ + +static void +handle_frame_param (struct frame *f, Lisp_Object prop, Lisp_Object val, + Lisp_Object old_value) +{ + Lisp_Object param_index = Fget (prop, Qx_frame_parameter); + if (FIXNATP (param_index) && XFIXNAT (param_index) < ARRAYELTS (frame_parms)) + { + if (FRAME_RIF (f)) + { + frame_parm_handler handler + = FRAME_RIF (f)->frame_parm_handlers[XFIXNAT (param_index)]; + if (handler) + handler (f, val, old_value); + } + } +} + /* Change the parameters of frame F as specified by ALIST. If a parameter is not specially recognized, do nothing special; otherwise call the `gui_set_...' function for that parameter. @@ -4387,17 +4660,9 @@ gui_set_frame_parameters_1 (struct frame *f, Lisp_Object alist, } else { - Lisp_Object param_index, old_value; - - old_value = get_frame_param (f, prop); - + Lisp_Object old_value = get_frame_param (f, prop); store_frame_param (f, prop, val); - - param_index = Fget (prop, Qx_frame_parameter); - if (FIXNATP (param_index) - && XFIXNAT (param_index) < ARRAYELTS (frame_parms) - && FRAME_RIF (f)->frame_parm_handlers[XFIXNUM (param_index)]) - (*(FRAME_RIF (f)->frame_parm_handlers[XFIXNUM (param_index)])) (f, val, old_value); + handle_frame_param (f, prop, val, old_value); if (!default_parameter && EQ (prop, Qfont)) /* The user manually specified the `font' frame parameter. @@ -4716,14 +4981,7 @@ gui_set_screen_gamma (struct frame *f, Lisp_Object new_value, Lisp_Object old_va /* Apply the new gamma value to the frame background. */ bgcolor = Fassq (Qbackground_color, f->param_alist); if (CONSP (bgcolor) && (bgcolor = XCDR (bgcolor), STRINGP (bgcolor))) - { - Lisp_Object parm_index = Fget (Qbackground_color, Qx_frame_parameter); - if (FIXNATP (parm_index) - && XFIXNAT (parm_index) < ARRAYELTS (frame_parms) - && FRAME_RIF (f)->frame_parm_handlers[XFIXNAT (parm_index)]) - (*FRAME_RIF (f)->frame_parm_handlers[XFIXNAT (parm_index)]) - (f, bgcolor, Qnil); - } + handle_frame_param (f, Qbackground_color, bgcolor, Qnil); clear_face_cache (true); /* FIXME: Why of all frames? */ fset_redisplay (f); @@ -6455,17 +6713,13 @@ syms_of_frame (void) DEFSYM (Quse_frame_synchronization, "use-frame-synchronization"); DEFSYM (Qfont_parameter, "font-parameter"); - { - int i; - - for (i = 0; i < ARRAYELTS (frame_parms); i++) - { - Lisp_Object v = (frame_parms[i].sym < 0 - ? intern_c_string (frame_parms[i].name) - : builtin_lisp_symbol (frame_parms[i].sym)); - Fput (v, Qx_frame_parameter, make_fixnum (i)); - } - } + for (int i = 0; i < ARRAYELTS (frame_parms); i++) + { + int sym = frame_parms[i].sym; + eassert (sym >= 0 && sym < ARRAYELTS (lispsym)); + Lisp_Object v = builtin_lisp_symbol (sym); + Fput (v, Qx_frame_parameter, make_fixnum (i)); + } #ifdef HAVE_WINDOW_SYSTEM DEFVAR_LISP ("x-resource-name", Vx_resource_name, modified src/frame.h @@ -161,10 +161,8 @@ #define EMACS_FRAME_H Usually it is nil. */ Lisp_Object title; -#if defined (HAVE_WINDOW_SYSTEM) /* This frame's parent frame, if it has one. */ Lisp_Object parent_frame; -#endif /* HAVE_WINDOW_SYSTEM */ /* Last device to move over this frame. Any value that isn't a string means the "Virtual core pointer". */ @@ -385,15 +383,8 @@ #define EMACS_FRAME_H zero if the frame has been made invisible without an icon. */ /* Nonzero if the frame is currently displayed; we check - it to see if we should bother updating the frame's contents. - - On ttys and on Windows NT/9X, to avoid wasting effort updating - visible frames that are actually completely obscured by other - windows on the display, we bend the meaning of visible slightly: - if equal to 2, then the frame is obscured - we still consider - it to be "visible" as seen from lisp, but we don't bother - updating it. */ - unsigned visible : 2; + it to see if we should bother updating the frame's contents. */ + unsigned visible : 1; /* True if the frame is currently iconified. Do not set this directly, use SET_FRAME_ICONIFIED instead. */ @@ -451,6 +442,13 @@ #define EMACS_FRAME_H This must be the same as the terminal->type. */ ENUM_BF (output_method) output_method : 4; + /* True if this is an undecorated frame. */ + bool_bf undecorated : 1; + + /* Nonzero if this frame's window does not want to receive input focus + via mouse clicks or by moving the mouse into it. */ + bool_bf no_accept_focus : 1; + #ifdef HAVE_WINDOW_SYSTEM /* True if this frame is a tooltip frame. */ bool_bf tooltip : 1; @@ -465,10 +463,7 @@ #define EMACS_FRAME_H /* Nonzero if we should actually display horizontal scroll bars on this frame. */ bool_bf horizontal_scroll_bars : 1; - /* True if this is an undecorated frame. */ - bool_bf undecorated : 1; - -#ifndef HAVE_NTGUI +# ifndef HAVE_NTGUI /* True if this is an override_redirect frame. */ bool_bf override_redirect : 1; #endif @@ -480,17 +475,13 @@ #define EMACS_FRAME_H receive input focus when it is mapped. */ bool_bf no_focus_on_map : 1; - /* Nonzero if this frame's window does not want to receive input focus - via mouse clicks or by moving the mouse into it. */ - bool_bf no_accept_focus : 1; - /* The z-group this frame's window belongs to. */ ENUM_BF (z_group) z_group : 2; +#endif /* HAVE_WINDOW_SYSTEM */ /* Non-zero if display of truncation and continuation glyphs outside the fringes is suppressed. */ bool_bf no_special_glyphs : 1; -#endif /* HAVE_WINDOW_SYSTEM */ /* True means set_window_size_hook requests can be processed for this frame. */ @@ -740,7 +731,10 @@ #define EMACS_FRAME_H #ifdef HAVE_TEXT_CONVERSION /* Text conversion state used by certain input methods. */ struct text_conversion_state conversion; -#endif +# endif + + /* Z-order of child frames. */ + int z_order; } GCALIGNED_STRUCT; /* Most code should use these functions to set Lisp fields in struct frame. */ @@ -1021,9 +1015,9 @@ #define FRAME_RES(f) default_pixels_per_inch_y () does not have FRAME_DISPLAY_INFO. */ #ifdef HAVE_WINDOW_SYSTEM #ifndef HAVE_ANDROID -# define MOUSE_HL_INFO(F) \ +# define MOUSE_HL_INFO(F) \ (FRAME_WINDOW_P (F) \ - ? &FRAME_DISPLAY_INFO(F)->mouse_highlight \ + ? &FRAME_DISPLAY_INFO (F)->mouse_highlight \ : &(F)->output_data.tty->display_info->mouse_highlight) #else /* There is no "struct tty_output" on Android at all. */ @@ -1176,9 +1170,6 @@ #define FRAME_REDISPLAY_P(f) (FRAME_VISIBLE_P (f) \ && FRAME_X_VISIBLE (f))) #endif -/* True if frame F is currently visible but hidden. */ -#define FRAME_OBSCURED_P(f) ((f)->visible > 1) - /* True if frame F is currently iconified. */ #define FRAME_ICONIFIED_P(f) (f)->iconified @@ -1243,21 +1234,23 @@ #define FRAME_HAS_VERTICAL_SCROLL_BARS_ON_LEFT(f) ((void) (f), 0) #define FRAME_HAS_VERTICAL_SCROLL_BARS_ON_RIGHT(f) ((void) (f), 0) #endif /* HAVE_WINDOW_SYSTEM */ -#if defined (HAVE_WINDOW_SYSTEM) +INLINE struct frame * +FRAME_PARENT_FRAME (struct frame *f) +{ + return NILP (f->parent_frame) ? NULL : XFRAME (f->parent_frame); +} + #define FRAME_UNDECORATED(f) ((f)->undecorated) + +#if defined (HAVE_WINDOW_SYSTEM) #ifdef HAVE_NTGUI #define FRAME_OVERRIDE_REDIRECT(f) ((void) (f), 0) #else #define FRAME_OVERRIDE_REDIRECT(f) ((f)->override_redirect) #endif -#define FRAME_PARENT_FRAME(f) \ - (NILP ((f)->parent_frame) \ - ? NULL \ - : XFRAME ((f)->parent_frame)) #define FRAME_SKIP_TASKBAR(f) ((f)->skip_taskbar) #define FRAME_NO_FOCUS_ON_MAP(f) ((f)->no_focus_on_map) #define FRAME_NO_ACCEPT_FOCUS(f) ((f)->no_accept_focus) -#define FRAME_NO_SPECIAL_GLYPHS(f) ((f)->no_special_glyphs) #define FRAME_Z_GROUP(f) ((f)->z_group) #define FRAME_Z_GROUP_NONE(f) ((f)->z_group == z_group_none) #define FRAME_Z_GROUP_ABOVE(f) ((f)->z_group == z_group_above) @@ -1270,13 +1263,10 @@ #define FRAME_NS_APPEARANCE(f) ((f)->ns_appearance) #define FRAME_NS_TRANSPARENT_TITLEBAR(f) ((f)->ns_transparent_titlebar) #endif #else /* not HAVE_WINDOW_SYSTEM */ -#define FRAME_UNDECORATED(f) ((void) (f), 0) #define FRAME_OVERRIDE_REDIRECT(f) ((void) (f), 0) -#define FRAME_PARENT_FRAME(f) ((void) (f), NULL) #define FRAME_SKIP_TASKBAR(f) ((void) (f), 0) #define FRAME_NO_FOCUS_ON_MAP(f) ((void) (f), 0) #define FRAME_NO_ACCEPT_FOCUS(f) ((void) (f), 0) -#define FRAME_NO_SPECIAL_GLYPHS(f) ((void) (f), 0) #define FRAME_Z_GROUP(f) ((void) (f), z_group_none) #define FRAME_Z_GROUP_NONE(f) ((void) (f), true) #define FRAME_Z_GROUP_ABOVE(f) ((void) (f), false) @@ -1284,6 +1274,8 @@ #define FRAME_Z_GROUP_BELOW(f) ((void) (f), false) #define FRAME_TOOLTIP_P(f) ((void) f, false) #endif /* HAVE_WINDOW_SYSTEM */ +#define FRAME_NO_SPECIAL_GLYPHS(f) ((f)->no_special_glyphs) + /* Whether horizontal scroll bars are currently enabled for frame F. */ #if USE_HORIZONTAL_SCROLL_BARS #define FRAME_HAS_HORIZONTAL_SCROLL_BARS(f) \ @@ -1445,9 +1437,8 @@ #define AUTO_FRAME_ARG(name, parameter, value) \ if some changes were applied to it while it wasn't visible (and hence wasn't redisplayed). */ INLINE void -SET_FRAME_VISIBLE (struct frame *f, int v) +SET_FRAME_VISIBLE (struct frame *f, bool v) { - eassert (0 <= v && v <= 2); if (v) { if (v == 1 && f->visible != 1) @@ -1504,13 +1495,14 @@ SET_FRAME_ICONIFIED (struct frame *f, int i) extern struct frame *make_initial_frame (void); extern struct frame *make_frame (bool); #ifdef HAVE_WINDOW_SYSTEM -extern struct frame *make_minibuffer_frame (void); -extern struct frame *make_frame_without_minibuffer (Lisp_Object, - struct kboard *, - Lisp_Object); extern bool display_available (void); #endif +struct frame *make_minibuffer_frame (void); +struct frame * +make_frame_without_minibuffer (Lisp_Object mini_window, + KBOARD *kb, Lisp_Object display); + INLINE bool window_system_available (struct frame *f) { @@ -1522,6 +1514,8 @@ window_system_available (struct frame *f) } extern WINDOW_SYSTEM_RETURN void check_window_system (struct frame *); +void check_tty (struct frame *f); +struct frame *decode_tty_frame (Lisp_Object frame); extern void frame_make_pointer_invisible (struct frame *); extern void frame_make_pointer_visible (struct frame *); extern Lisp_Object delete_frame (Lisp_Object, Lisp_Object); @@ -1617,15 +1611,11 @@ FRAME_CHILD_FRAME_BORDER_WIDTH (struct frame *f) INLINE int FRAME_INTERNAL_BORDER_WIDTH (struct frame *f) { -#ifdef HAVE_WINDOW_SYSTEM return (FRAME_PARENT_FRAME(f) ? (FRAME_CHILD_FRAME_BORDER_WIDTH(f) >= 0 ? FRAME_CHILD_FRAME_BORDER_WIDTH(f) : frame_dimension (f->internal_border_width)) : frame_dimension (f->internal_border_width)); -#else - return frame_dimension (f->internal_border_width); -#endif } /* Pixel-size of window divider lines. */ @@ -1880,7 +1870,6 @@ #define EMACS_CLASS "Emacs" extern void set_frame_menubar (struct frame *f, bool deep_p); extern void frame_set_mouse_pixel_position (struct frame *f, int pix_x, int pix_y); extern void free_frame_menubar (struct frame *); -extern bool frame_ancestor_p (struct frame *af, struct frame *df); extern enum internal_border_part frame_internal_border_part (struct frame *f, int x, int y); #if defined HAVE_X_WINDOWS @@ -1907,6 +1896,8 @@ gui_set_bitmap_icon (struct frame *f) #endif /* !HAVE_NS */ #endif /* HAVE_WINDOW_SYSTEM */ +extern bool frame_ancestor_p (struct frame *af, struct frame *df); + INLINE void flush_frame (struct frame *f) { modified src/keyboard.c @@ -5415,7 +5415,7 @@ #define FUNCTION_KEY_OFFSET 0xff00 /* You'll notice that this table is arranged to be conveniently indexed by X Windows keysym values. */ -#ifdef HAVE_NS +#if defined HAVE_NS || !defined HAVE_WINDOW_SYSTEM /* FIXME: Why are we using X11 keysym values for NS? */ static #endif modified src/minibuf.c @@ -913,7 +913,11 @@ read_minibuf (Lisp_Object map, Lisp_Object initial, Lisp_Object prompt, XWINDOW (minibuf_window)->cursor.hpos = 0; XWINDOW (minibuf_window)->cursor.x = 0; XWINDOW (minibuf_window)->must_be_updated_p = true; - update_frame (XFRAME (selected_frame), true, true); + struct frame *sf = XFRAME (selected_frame); + update_frame (sf, true, true); + if (is_tty_frame (sf)) + combine_updates_for_frame (sf, true, true); + #ifndef HAVE_NTGUI flush_frame (XFRAME (XWINDOW (minibuf_window)->frame)); #else modified src/nsfns.m @@ -3351,7 +3351,7 @@ internalBorderWidth or internalBorder (which is what xterm calls [nswindow orderFront: NSApp]; [nswindow display]; - SET_FRAME_VISIBLE (tip_f, 1); + SET_FRAME_VISIBLE (tip_f, true); unblock_input (); goto start_timer; @@ -3534,7 +3534,7 @@ internalBorderWidth or internalBorder (which is what xterm calls [nswindow orderFront: NSApp]; [nswindow display]; - SET_FRAME_VISIBLE (tip_f, YES); + SET_FRAME_VISIBLE (tip_f, true); FRAME_PIXEL_WIDTH (tip_f) = width; FRAME_PIXEL_HEIGHT (tip_f) = height; unblock_input (); modified src/nsterm.m @@ -1505,7 +1505,7 @@ -(void)remove EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f); EmacsWindow *window = (EmacsWindow *)[view window]; - SET_FRAME_VISIBLE (f, 1); + SET_FRAME_VISIBLE (f, true); ns_raise_frame (f, ! FRAME_NO_FOCUS_ON_MAP (f)); /* Making a new frame from a fullscreen frame will make the new frame @@ -1550,7 +1550,7 @@ Hide the window (X11 semantics) check_window_system (f); view = FRAME_NS_VIEW (f); [[view window] orderOut: NSApp]; - SET_FRAME_VISIBLE (f, 0); + SET_FRAME_VISIBLE (f, false); SET_FRAME_ICONIFIED (f, 0); } modified src/scroll.c @@ -366,7 +366,7 @@ do_scrolling (struct frame *frame, struct glyph_matrix *current_matrix, eassert (copy_from[k] >= 0 && copy_from[k] < window_size); /* Perform the row swizzling. */ - mirrored_line_dance (current_matrix, unchanged_at_top, window_size, + mirrored_line_dance (frame, unchanged_at_top, window_size, copy_from, retained_p); /* Some sanity checks if GLYPH_DEBUG is defined. */ @@ -780,7 +780,7 @@ do_direct_scrolling (struct frame *frame, struct glyph_matrix *current_matrix, copy_from[i] gives the original line to copy to I, and retained_p[copy_from[i]] is zero if line I in the new display is empty. */ - mirrored_line_dance (current_matrix, unchanged_at_top, window_size, + mirrored_line_dance (frame, unchanged_at_top, window_size, copy_from, retained_p); if (terminal_window_p) modified src/term.c @@ -65,11 +65,9 @@ #ifndef HAVE_ANDROID static void tty_set_scroll_region (struct frame *f, int start, int stop); -static void turn_on_face (struct frame *, int face_id); -static void turn_off_face (struct frame *, int face_id); +static void turn_on_face (struct frame *f, struct face *face); +static void turn_off_face (struct frame *f, struct face *face); static void tty_turn_off_highlight (struct tty_display_info *); -static void tty_show_cursor (struct tty_display_info *); -static void tty_hide_cursor (struct tty_display_info *); static void tty_background_highlight (struct tty_display_info *tty); static void clear_tty_hooks (struct terminal *terminal); static void set_tty_hooks (struct terminal *terminal); @@ -336,7 +334,7 @@ tty_toggle_highlight (struct tty_display_info *tty) /* Make cursor invisible. */ -static void +void tty_hide_cursor (struct tty_display_info *tty) { if (tty->cursor_hidden == 0) @@ -353,7 +351,7 @@ tty_hide_cursor (struct tty_display_info *tty) /* Ensure that cursor is visible. */ -static void +void tty_show_cursor (struct tty_display_info *tty) { if (tty->cursor_hidden) @@ -753,18 +751,12 @@ encode_terminal_code (struct glyph *src, int src_len, static void tty_write_glyphs (struct frame *f, struct glyph *string, int len) { - unsigned char *conversion_buffer; - struct coding_system *coding; - int n, stringlen; - struct tty_display_info *tty = FRAME_TTY (f); - tty_turn_off_insert (tty); tty_hide_cursor (tty); /* Don't dare write in last column of bottom line, if Auto-Wrap, since that would scroll the whole frame on some terminals. */ - if (AutoWrap (tty) && curY (tty) + 1 == FRAME_TOTAL_LINES (f) && (curX (tty) + len) == FRAME_COLS (f)) @@ -777,29 +769,34 @@ tty_write_glyphs (struct frame *f, struct glyph *string, int len) /* If terminal_coding does any conversion, use it, otherwise use safe_terminal_coding. We can't use CODING_REQUIRE_ENCODING here because it always return 1 if the member src_multibyte is 1. */ - coding = (FRAME_TERMINAL_CODING (f)->common_flags & CODING_REQUIRE_ENCODING_MASK - ? FRAME_TERMINAL_CODING (f) : &safe_terminal_coding); + struct coding_system *coding + = (FRAME_TERMINAL_CODING (f)->common_flags & CODING_REQUIRE_ENCODING_MASK + ? FRAME_TERMINAL_CODING (f) : &safe_terminal_coding); + /* The mode bit CODING_MODE_LAST_BLOCK should be set to 1 only at the tail. */ coding->mode &= ~CODING_MODE_LAST_BLOCK; - for (stringlen = len; stringlen != 0; stringlen -= n) + for (int stringlen = len, n; stringlen; stringlen -= n, string += n) { /* Identify a run of glyphs with the same face. */ int face_id = string->face_id; + struct frame *face_id_frame = string->frame; for (n = 1; n < stringlen; ++n) - if (string[n].face_id != face_id) + if (string[n].face_id != face_id || string[n].frame != face_id_frame) break; /* Turn appearance modes of the face of the run on. */ tty_highlight_if_desired (tty); - turn_on_face (f, face_id); + struct face *face = FACE_FROM_ID (face_id_frame, face_id); + turn_on_face (f, face); if (n == stringlen) /* This is the last run. */ coding->mode |= CODING_MODE_LAST_BLOCK; - conversion_buffer = encode_terminal_code (string, n, coding); + unsigned char *conversion_buffer + = encode_terminal_code (string, n, coding); if (coding->produced > 0) { block_input (); @@ -809,10 +806,9 @@ tty_write_glyphs (struct frame *f, struct glyph *string, int len) fwrite (conversion_buffer, 1, coding->produced, tty->termscript); unblock_input (); } - string += n; /* Turn appearance modes off. */ - turn_off_face (f, face_id); + turn_off_face (f, face); tty_turn_off_highlight (tty); } @@ -822,8 +818,8 @@ tty_write_glyphs (struct frame *f, struct glyph *string, int len) #ifndef DOS_NT static void -tty_write_glyphs_with_face (register struct frame *f, register struct glyph *string, - register int len, register int face_id) +tty_write_glyphs_with_face (struct frame *f, struct glyph *string, + int len, struct face *face) { unsigned char *conversion_buffer; struct coding_system *coding; @@ -856,7 +852,7 @@ tty_write_glyphs_with_face (register struct frame *f, register struct glyph *str /* Turn appearance modes of the face. */ tty_highlight_if_desired (tty); - turn_on_face (f, face_id); + turn_on_face (f, face); coding->mode |= CODING_MODE_LAST_BLOCK; conversion_buffer = encode_terminal_code (string, len, coding); @@ -871,7 +867,7 @@ tty_write_glyphs_with_face (register struct frame *f, register struct glyph *str } /* Turn appearance modes off. */ - turn_off_face (f, face_id); + turn_off_face (f, face); tty_turn_off_highlight (tty); cmcheckmagic (tty); @@ -919,6 +915,7 @@ tty_insert_glyphs (struct frame *f, struct glyph *start, int len) while (len-- > 0) { + struct face *face = NULL; OUTPUT1_IF (tty, tty->TS_ins_char); if (!start) { @@ -928,7 +925,10 @@ tty_insert_glyphs (struct frame *f, struct glyph *start, int len) else { tty_highlight_if_desired (tty); - turn_on_face (f, start->face_id); + int face_id = start->face_id; + struct frame *face_id_frame = start->frame; + face = FACE_FROM_ID (face_id_frame, face_id); + turn_on_face (f, face); glyph = start; ++start; /* We must open sufficient space for a character which @@ -957,9 +957,9 @@ tty_insert_glyphs (struct frame *f, struct glyph *start, int len) } OUTPUT1_IF (tty, tty->TS_pad_inserted_char); - if (start) + if (face) { - turn_off_face (f, glyph->face_id); + turn_off_face (f, face); tty_turn_off_highlight (tty); } } @@ -1542,6 +1542,7 @@ append_glyph (struct it *it) glyph->type = CHAR_GLYPH; glyph->pixel_width = 1; glyph->u.ch = it->char_to_display; + glyph->frame = it->f; glyph->face_id = it->face_id; glyph->avoid_cursor_p = it->avoid_cursor_p; glyph->multibyte_p = it->multibyte_p; @@ -1769,6 +1770,7 @@ append_composite_glyph (struct it *it) glyph->avoid_cursor_p = it->avoid_cursor_p; glyph->multibyte_p = it->multibyte_p; + glyph->frame = it->f; glyph->face_id = it->face_id; glyph->padding_p = false; glyph->charpos = CHARPOS (it->position); @@ -1855,6 +1857,7 @@ append_glyphless_glyph (struct it *it, int face_id, const char *str) glyph->pixel_width = 1; glyph->avoid_cursor_p = it->avoid_cursor_p; glyph->multibyte_p = it->multibyte_p; + glyph->frame = it->f; glyph->face_id = face_id; glyph->padding_p = false; glyph->charpos = CHARPOS (it->position); @@ -1981,9 +1984,8 @@ #define MAY_USE_WITH_COLORS_P(tty, ATTR) \ FACE_ID is a realized face ID number, in the face cache. */ static void -turn_on_face (struct frame *f, int face_id) +turn_on_face (struct frame *f, struct face *face) { - struct face *face = FACE_FROM_ID (f, face_id); unsigned long fg = face->foreground; unsigned long bg = face->background; struct tty_display_info *tty = FRAME_TTY (f); @@ -2064,9 +2066,8 @@ turn_on_face (struct frame *f, int face_id) /* Turn off appearances of face FACE_ID on tty frame F. */ static void -turn_off_face (struct frame *f, int face_id) +turn_off_face (struct frame *f, struct face *face) { - struct face *face = FACE_FROM_ID (f, face_id); struct tty_display_info *tty = FRAME_TTY (f); if (tty->TS_exit_attribute_mode) @@ -2399,8 +2400,10 @@ DEFUN ("suspend-tty", Fsuspend_tty, Ssuspend_tty, 0, 1, 0, t->display_info.tty->output = 0; if (FRAMEP (t->display_info.tty->top_frame)) - SET_FRAME_VISIBLE (XFRAME (t->display_info.tty->top_frame), 0); - + { + struct frame *top = XFRAME (t->display_info.tty->top_frame); + SET_FRAME_VISIBLE (root_frame (top), false); + } } /* Clear display hooks to prevent further output. */ @@ -2472,7 +2475,8 @@ DEFUN ("resume-tty", Fresume_tty, Sresume_tty, 0, 1, 0, if (FRAMEP (t->display_info.tty->top_frame)) { - struct frame *f = XFRAME (t->display_info.tty->top_frame); + struct frame *top = XFRAME (t->display_info.tty->top_frame); + struct frame *f = root_frame (top); int width, height; int old_height = FRAME_COLS (f); int old_width = FRAME_TOTAL_LINES (f); @@ -2482,7 +2486,7 @@ DEFUN ("resume-tty", Fresume_tty, Sresume_tty, 0, 1, 0, get_tty_size (fileno (t->display_info.tty->input), &width, &height); if (width != old_width || height != old_height) change_frame_size (f, width, height, false, false, false); - SET_FRAME_VISIBLE (XFRAME (t->display_info.tty->top_frame), 1); + SET_FRAME_VISIBLE (f, true); } set_tty_hooks (t); @@ -2563,22 +2567,24 @@ tty_draw_row_with_mouse_face (struct window *w, struct glyph_row *row, struct frame *f = XFRAME (WINDOW_FRAME (w)); struct tty_display_info *tty = FRAME_TTY (f); int face_id = tty->mouse_highlight.mouse_face_face_id; - int save_x, save_y, pos_x, pos_y; if (end_hpos >= row->used[TEXT_AREA]) nglyphs = row->used[TEXT_AREA] - start_hpos; - pos_y = row->y + WINDOW_TOP_EDGE_Y (w); - pos_x = row->used[LEFT_MARGIN_AREA] + start_hpos + WINDOW_LEFT_EDGE_X (w); + int pos_y = row->y + WINDOW_TOP_EDGE_Y (w); + int pos_x = row->used[LEFT_MARGIN_AREA] + start_hpos + WINDOW_LEFT_EDGE_X (w); /* Save current cursor coordinates. */ - save_y = curY (tty); - save_x = curX (tty); + int save_y = curY (tty); + int save_x = curX (tty); cursor_to (f, pos_y, pos_x); if (draw == DRAW_MOUSE_FACE) - tty_write_glyphs_with_face (f, row->glyphs[TEXT_AREA] + start_hpos, - nglyphs, face_id); + { + struct glyph *glyph = row->glyphs[TEXT_AREA] + start_hpos; + struct face *face = FACE_FROM_ID (f, face_id); + tty_write_glyphs_with_face (f, glyph, nglyphs, face); + } else if (draw == DRAW_NORMAL_TEXT) write_glyphs (f, row->glyphs[TEXT_AREA] + start_hpos, nglyphs); @@ -3115,9 +3121,7 @@ save_and_enable_current_matrix (struct frame *f) static void restore_desired_matrix (struct frame *f, struct glyph_matrix *saved) { - int i; - - for (i = 0; i < saved->nrows; ++i) + for (int i = 0; i < saved->nrows; ++i) { struct glyph_row *from = saved->rows + i; struct glyph_row *to = f->desired_matrix->rows + i; @@ -3127,7 +3131,23 @@ restore_desired_matrix (struct frame *f, struct glyph_matrix *saved) memcpy (to->glyphs[TEXT_AREA], from->glyphs[TEXT_AREA], nbytes); to->used[TEXT_AREA] = from->used[TEXT_AREA]; to->enabled_p = from->enabled_p; - to->hash = from->hash; + + bool need_new_hash = false; + for (int x = 0; x < f->desired_matrix->matrix_w; ++x) + { + struct glyph *glyph = to->glyphs[0] + x; + if (!FRAME_LIVE_P (glyph->frame)) + { + glyph->frame = f; + glyph->face_id = DEFAULT_FACE_ID; + need_new_hash = true; + } + } + + if (need_new_hash) + to->hash = row_hash (to); + else + to->hash = from->hash; } } @@ -3969,7 +3989,7 @@ tty_free_frame_resources (struct frame *f) #endif - + #ifndef HAVE_ANDROID @@ -4044,6 +4064,8 @@ set_tty_hooks (struct terminal *terminal) terminal->read_socket_hook = &tty_read_avail_input; /* keyboard.c */ terminal->delete_frame_hook = &tty_free_frame_resources; terminal->delete_terminal_hook = &delete_tty; + + terminal->frame_raise_lower_hook = tty_raise_lower_frame; /* Other hooks are NULL by default. */ } @@ -4714,6 +4736,186 @@ delete_tty (struct terminal *terminal) #endif +/* Return geometric attributes of FRAME. According to the value of + ATTRIBUTES return the outer edges of FRAME (Qouter_edges), the + native edges of FRAME (Qnative_edges), or the inner edges of frame + (Qinner_edges). Any other value means to return the geometry as + returned by Fx_frame_geometry. */ + +static Lisp_Object +tty_frame_geometry (Lisp_Object frame, Lisp_Object attribute) +{ + struct frame *f = decode_live_frame (frame); + if (FRAME_INITIAL_P (f) || !FRAME_TTY (f)) + return Qnil; + + int native_width = f->pixel_width; + int native_height = f->pixel_height; + + eassert (FRAME_PARENT_FRAME (f) || (f->left_pos == 0 && f->top_pos == 0)); + int outer_left = f->left_pos; + int outer_top = f->top_pos; + int outer_right = outer_left + native_width; + int outer_bottom = outer_top + native_height; + + int native_left = outer_left; + int native_top = outer_top; + int native_right = outer_right; + int native_bottom = outer_bottom; + + int internal_border_width = FRAME_INTERNAL_BORDER_WIDTH (f); + int inner_left = native_left + internal_border_width; + int inner_top = native_top + internal_border_width; + int inner_right = native_right - internal_border_width; + int inner_bottom = native_bottom - internal_border_width; + + int menu_bar_height = FRAME_MENU_BAR_HEIGHT (f); + inner_top += menu_bar_height; + int menu_bar_width = menu_bar_height ? native_width : 0; + + int tab_bar_height = FRAME_TAB_BAR_HEIGHT (f); + int tab_bar_width = (tab_bar_height + ? native_width - 2 * internal_border_width + : 0); + inner_top += tab_bar_height; + + int tool_bar_height = FRAME_TOOL_BAR_HEIGHT (f); + int tool_bar_width = (tool_bar_height + ? native_width - 2 * internal_border_width + : 0); + + /* Subtract or add to the inner dimensions based on the tool bar + position. */ + if (EQ (FRAME_TOOL_BAR_POSITION (f), Qtop)) + inner_top += tool_bar_height; + else + inner_bottom -= tool_bar_height; + + /* Construct list. */ + if (EQ (attribute, Qouter_edges)) + return list4i (outer_left, outer_top, outer_right, outer_bottom); + else if (EQ (attribute, Qnative_edges)) + return list4i (native_left, native_top, native_right, native_bottom); + else if (EQ (attribute, Qinner_edges)) + return list4i (inner_left, inner_top, inner_right, inner_bottom); + else + return list (Fcons (Qouter_position, Fcons (make_fixnum (outer_left), + make_fixnum (outer_top))), + Fcons (Qouter_size, + Fcons (make_fixnum (outer_right - outer_left), + make_fixnum (outer_bottom - outer_top))), + Fcons (Qouter_border_width, make_fixnum (0)), + Fcons (Qexternal_border_size, + Fcons (make_fixnum (0), make_fixnum (0))), + Fcons (Qtitle_bar_size, + Fcons (make_fixnum (0), make_fixnum (0))), + Fcons (Qmenu_bar_external, Qnil), + Fcons (Qmenu_bar_size, + Fcons (make_fixnum (menu_bar_width), + make_fixnum (menu_bar_height))), + Fcons (Qtab_bar_size, + Fcons (make_fixnum (tab_bar_width), + make_fixnum (tab_bar_height))), + Fcons (Qtool_bar_external, Qnil), + Fcons (Qtool_bar_position, FRAME_TOOL_BAR_POSITION (f)), + Fcons (Qtool_bar_size, + Fcons (make_fixnum (tool_bar_width), + make_fixnum (tool_bar_height))), + Fcons (Qinternal_border_width, + make_fixnum (internal_border_width))); +} + +DEFUN ("tty-frame-geometry", Ftty_frame_geometry, Stty_frame_geometry, 0, 1, 0, + doc: /* Return geometric attributes of terminal frame FRAME. + See also `frame-geometry'. */) + (Lisp_Object frame) +{ + return tty_frame_geometry (frame, Qnil); +} + +DEFUN ("tty-frame-edges", Ftty_frame_edges, Stty_frame_edges, 0, 2, 0, + doc: /* Return coordinates of FRAME's edges. + See also `frame-edges'. */) + (Lisp_Object frame, Lisp_Object type) +{ + if (!EQ (type, Qouter_edges) && !EQ (type, Qinner_edges)) + type = Qnative_edges; + return tty_frame_geometry (frame, type); +} + +DEFUN ("tty-frame-list-z-order", Ftty_frame_list_z_order, + Stty_frame_list_z_order, 0, 1, 0, + doc: /* Return list of Emacs's frames, in Z (stacking) order. + See also `frame-list-z-order'. */) + (Lisp_Object frame) +{ + struct frame *f = decode_tty_frame (frame); + Lisp_Object frames = frames_in_reverse_z_order (f, true); + return Fnreverse (frames); +} + +DEFUN ("tty-frame-restack", Ftty_frame_restack, + Stty_frame_restack, 2, 3, 0, + doc: /* Restack FRAME1 below FRAME2 on terminals. +. See also `frame-restack'. */) + (Lisp_Object frame1, Lisp_Object frame2, Lisp_Object above) +{ + /* FIXME/tty: tty-frame-restack implementation. */ + error ("tty-frame-restack is not implemented"); + return Qnil; +} + +static void +tty_display_dimension (Lisp_Object frame, int *width, int *height) +{ + if (!FRAMEP (frame)) + frame = Fselected_frame (); + struct frame *f = XFRAME (frame); + switch (f->output_method) + { + case output_initial: + *width = 80; + *height = 25; + break; + case output_termcap: + *width = FrameCols (FRAME_TTY (f)); + *height = FrameRows (FRAME_TTY (f)); + break; + case output_x_window: + case output_msdos_raw: + case output_w32: + case output_ns: + case output_pgtk: + case output_haiku: + case output_android: + default: + emacs_abort (); + break; + } +} + +DEFUN ("tty-display-pixel-width", Ftty_display_pixel_width, + Stty_display_pixel_width, 0, 1, 0, + doc: /* Return the width of DISPLAY's screen in pixels. +. See also `display-pixel-width'. */) + (Lisp_Object display) +{ + int width, height; + tty_display_dimension (display, &width, &height); + return make_fixnum (width); +} + +DEFUN ("tty-display-pixel-height", Ftty_display_pixel_height, + Stty_display_pixel_height, 0, 1, 0, + doc: /* Return the height of DISPLAY's screen in pixels. + See also `display-pixel-height'. */) + (Lisp_Object display) +{ + int width, height; + tty_display_dimension (display, &width, &height); + return make_fixnum (height); +} + void syms_of_term (void) { @@ -4770,6 +4972,13 @@ syms_of_term (void) defsubr (&Sgpm_mouse_stop); #endif /* HAVE_GPM */ + defsubr (&Stty_frame_geometry); + defsubr (&Stty_frame_edges); + defsubr (&Stty_frame_list_z_order); + defsubr (&Stty_frame_restack); + defsubr (&Stty_display_pixel_width); + defsubr (&Stty_display_pixel_height); + #if !defined DOS_NT && !defined HAVE_ANDROID default_orig_pair = NULL; default_set_foreground = NULL; modified src/termhooks.h @@ -974,6 +974,9 @@ #define cursorX(t) curX(t) #define cursorY(t) curY(t) #endif +void tty_hide_cursor (struct tty_display_info *tty); +void tty_show_cursor (struct tty_display_info *tty); + INLINE_HEADER_END #endif /* EMACS_TERMHOOKS_H */ modified src/terminal.c @@ -111,7 +111,8 @@ set_terminal_window (struct frame *f, int size) cursor_to (struct frame *f, int vpos, int hpos) { if (FRAME_TERMINAL (f)->cursor_to_hook) - (*FRAME_TERMINAL (f)->cursor_to_hook) (f, vpos, hpos); + (*FRAME_TERMINAL (f)->cursor_to_hook) (f, vpos + f->top_pos, + hpos + f->left_pos); } /* Similar but don't take any account of the wasted characters. */ @@ -120,7 +121,8 @@ cursor_to (struct frame *f, int vpos, int hpos) 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, col); + (*FRAME_TERMINAL (f)->raw_cursor_to_hook) (f, row + f->top_pos, + col + f->left_pos); } /* Erase operations. */ modified src/treesit.c @@ -651,7 +651,7 @@ treesit_load_language (Lisp_Object language_symbol, /* Override the library name and C name, if appropriate. */ Lisp_Object override_name; - Lisp_Object override_c_name; + Lisp_Object override_c_name UNINIT; bool found_override = treesit_find_override_name (language_symbol, &override_name, &override_c_name); modified src/w32console.c @@ -167,6 +167,7 @@ w32con_clear_end_of_line (struct frame *f, int end) for (i = 0; i < glyphs_len; i++) { memcpy (&glyphs[i], &space_glyph, sizeof (struct glyph)); + glyphs[i].frame = f; } ceol_initialized = TRUE; } @@ -327,14 +328,19 @@ w32con_write_glyphs (struct frame *f, register struct glyph *string, { /* Identify a run of glyphs with the same face. */ int face_id = string->face_id; + /* Since this is called to deliver the frame glyph matrix to the + glass, some of the glyphs might be from a child frame, which + affects the interpretation of face ID. */ + struct frame *face_id_frame = string->frame; int n; for (n = 1; n < len; ++n) - if (string[n].face_id != face_id) + if (!(string[n].face_id == face_id + && string[n].frame == face_id_frame)) break; /* Turn appearance modes of the face of the run on. */ - char_attr = w32_face_attributes (f, face_id); + char_attr = w32_face_attributes (face_id_frame, face_id); if (n == len) /* This is the last run. */ @@ -530,6 +536,11 @@ w32con_update_begin (struct frame * f) w32con_update_end (struct frame * f) { SetConsoleCursorPosition (cur_screen, cursor_coords); + if (!XWINDOW (selected_window)->cursor_off_p + && cursor_coords.X < FRAME_COLS (f)) + w32con_show_cursor (); + else + w32con_hide_cursor (); } /*********************************************************************** modified src/w32inevt.c @@ -471,8 +471,13 @@ do_mouse_event (MOUSE_EVENT_RECORD *event, DWORD but_change, mask, flags = event->dwEventFlags; int i; - /* Mouse didn't move unless MOUSE_MOVED says it did. */ struct frame *f = get_frame (); + + /* For now, mouse events on child frames are ignored, because the + coordinate conversion is not in place; FIXME. */ + if (FRAMEP (f->parent_frame)) + return 0; + /* Mouse didn't move unless MOUSE_MOVED says it did. */ f->mouse_moved = 0; switch (flags) @@ -619,6 +624,10 @@ maybe_generate_resize_event (void) CONSOLE_SCREEN_BUFFER_INFO info; struct frame *f = get_frame (); + /* Only resize the root frame. */ + if (FRAMEP (f->parent_frame)) + return; + GetConsoleScreenBufferInfo (GetStdHandle (STD_OUTPUT_HANDLE), &info); /* It is okay to call this unconditionally, since it will do nothing modified src/w32term.c @@ -6405,14 +6405,13 @@ w32_read_socket (struct terminal *terminal, if (FRAME_TOOLTIP_P (f)) continue; - /* Check "visible" frames and mark each as obscured or not. + /* Check "visible" frames and mark each as visible or not. Note that visible is nonzero for unobscured and obscured frames, but zero for hidden and iconified frames. */ if (FRAME_W32_P (f) && FRAME_VISIBLE_P (f)) { RECT clipbox; HDC hdc; - bool obscured; enter_crit (); /* Query clipping rectangle for the entire window area @@ -6426,29 +6425,11 @@ w32_read_socket (struct terminal *terminal, ReleaseDC (FRAME_W32_WINDOW (f), hdc); leave_crit (); - obscured = FRAME_OBSCURED_P (f); - - if (clipbox.right == clipbox.left || clipbox.bottom == clipbox.top) - { - /* Frame has become completely obscured so mark as such (we - do this by setting visible to 2 so that FRAME_VISIBLE_P - is still true, but redisplay will skip it). */ - SET_FRAME_VISIBLE (f, 2); - - if (!obscured) - DebPrint (("frame %p (%s) obscured\n", f, SDATA (f->name))); - } - else + if (!(clipbox.right == clipbox.left + || clipbox.bottom == clipbox.top)) { /* Frame is not obscured, so mark it as such. */ SET_FRAME_VISIBLE (f, 1); - - if (obscured) - { - SET_FRAME_GARBAGED (f); - DebPrint (("obscured frame %p (%s) found to be visible\n", - f, SDATA (f->name))); - } } } } modified src/xdisp.c @@ -943,7 +943,7 @@ redisplay_trace (char const *fmt, ...) { va_list ap; va_start (ap, fmt); - vprintf (fmt, ap); + vfprintf (stderr, fmt, ap); va_end (ap); } } @@ -961,7 +961,7 @@ move_trace (char const *fmt, ...) { va_list ap; va_start (ap, fmt); - vprintf (fmt, ap); + vfprintf (stderr, fmt, ap); va_end (ap); } } @@ -3348,9 +3348,7 @@ init_iterator (struct it *it, struct window *w, of the iterator's frame, when set, suppresses their display - by default for tooltip frames and when set via the 'no-special-glyphs' frame parameter. */ -#ifdef HAVE_WINDOW_SYSTEM - if (!(FRAME_WINDOW_P (it->f) && it->f->no_special_glyphs)) -#endif + if (!it->f->no_special_glyphs) { if (it->line_wrap == TRUNCATE) { @@ -13415,18 +13413,22 @@ clear_message (bool current_p, bool last_displayed_p) message_buf_print = false; } -/* Clear garbaged frames. +/* Clear garbaged frames. Value is true if current matrices have been + cleared on at least one tty frame. This information is needed to + determine if more than one window has to be updated on ttys, whose + update requires building a frame matrix from window matrices. This function is used where the old redisplay called redraw_garbaged_frames which in turn called redraw_frame which in turn called clear_frame. The call to clear_frame was a source of flickering. I believe a clear_frame is not necessary. It should suffice in the new redisplay to invalidate all current matrices, - and ensure a complete redisplay of all windows. */ + and ensure a complete redisplay of all windows. */ -static void +static bool clear_garbaged_frames (void) { + bool current_matrices_cleared = false; if (frame_garbaged) { Lisp_Object tail, frame; @@ -13448,6 +13450,8 @@ clear_garbaged_frames (void) redraw_frame (f); else clear_current_matrices (f); + if (is_tty_frame (f)) + current_matrices_cleared = true; #ifdef HAVE_WINDOW_SYSTEM if (FRAME_WINDOW_P (f) @@ -13462,6 +13466,8 @@ clear_garbaged_frames (void) frame_garbaged = false; } + + return current_matrices_cleared; } @@ -13554,7 +13560,11 @@ echo_area_display (bool update_frame_p) flush_frame (f); } else - update_frame (f, true, true); + { + update_frame (f, true, true); + if (is_tty_frame (f)) + combine_updates_for_frame (f, true, true); + } /* If cursor is in the echo area, make sure that the next redisplay displays the minibuffer, so that the cursor will @@ -17033,16 +17043,22 @@ redisplay_internal (void) if (face_change) windows_or_buffers_changed = 47; + struct frame *previous_frame; if ((FRAME_TERMCAP_P (sf) || FRAME_MSDOS_P (sf)) - && FRAME_TTY (sf)->previous_frame != sf) + && (previous_frame = FRAME_TTY (sf)->previous_frame, + previous_frame != sf)) { - /* Since frames on a single ASCII terminal share the same - display area, displaying a different frame means redisplay - the whole thing. */ - SET_FRAME_GARBAGED (sf); + if (previous_frame == NULL + || root_frame (previous_frame) != root_frame (sf)) + { + /* Since frames on a single terminal share the same display + area, displaying a different frame means redisplay the + whole thing. */ + SET_FRAME_GARBAGED (sf); #if !defined DOS_NT && !defined HAVE_ANDROID - set_tty_color_mode (FRAME_TTY (sf), sf); + set_tty_color_mode (FRAME_TTY (sf), sf); #endif + } FRAME_TTY (sf)->previous_frame = sf; } @@ -17055,6 +17071,7 @@ redisplay_internal (void) { struct frame *f = XFRAME (frame); + /* FRAME_REDISPLAY_P true basically means the frame is visible. */ if (FRAME_REDISPLAY_P (f)) { ++number_of_visible_frames; @@ -17083,7 +17100,7 @@ redisplay_internal (void) do_pending_window_change (true); /* Clear frames marked as garbaged. */ - clear_garbaged_frames (); + bool current_matrices_cleared = clear_garbaged_frames (); /* Build menubar and tool-bar items. */ if (NILP (Vmemory_full)) @@ -17174,7 +17191,8 @@ redisplay_internal (void) overlay_arrows_changed_p (true); consider_all_windows_p = (update_mode_lines - || windows_or_buffers_changed); + || windows_or_buffers_changed + || current_matrices_cleared); #define AINC(a,i) \ { \ @@ -17198,7 +17216,6 @@ #define AINC(a,i) \ && !current_buffer->clip_changed && !current_buffer->prevent_redisplay_optimizations_p && FRAME_REDISPLAY_P (XFRAME (w->frame)) - && !FRAME_OBSCURED_P (XFRAME (w->frame)) && !XFRAME (w->frame)->cursor_type_changed && !XFRAME (w->frame)->face_change /* Make sure recorded data applies to current buffer, etc. */ @@ -17447,22 +17464,28 @@ #define AINC(a,i) \ propagate_buffer_redisplay (); + Lisp_Object tty_root_frames = Qnil; FOR_EACH_FRAME (tail, frame) { struct frame *f = XFRAME (frame); - /* We don't have to do anything for unselected terminal - frames. */ - if ((FRAME_TERMCAP_P (f) || FRAME_MSDOS_P (f)) - && !EQ (FRAME_TTY (f)->top_frame, frame)) - continue; + if (is_tty_frame (f)) + { + /* Ignore all invisble tty frames, children or root. */ + if (!FRAME_VISIBLE_P (root_frame (f))) + continue; + + /* Remember tty root frames which we've seen. */ + if (!FRAME_PARENT_FRAME (f) + && NILP (assq_no_quit (frame, tty_root_frames))) + tty_root_frames = Fcons (frame, tty_root_frames); + } retry_frame: if (FRAME_WINDOW_P (f) || FRAME_TERMCAP_P (f) || f == sf) { - bool gcscrollbars - /* Only GC scrollbars when we redisplay the whole frame. */ - = f->redisplay || !REDISPLAY_SOME_P (); + /* Only GC scrollbars when we redisplay the whole frame. */ + bool gcscrollbars = f->redisplay || !REDISPLAY_SOME_P (); bool f_redisplay_flag = f->redisplay; /* The X error handler may have deleted that frame before @@ -17479,7 +17502,7 @@ #define AINC(a,i) \ if (gcscrollbars && FRAME_TERMINAL (f)->condemn_scroll_bars_hook) FRAME_TERMINAL (f)->condemn_scroll_bars_hook (f); - if (FRAME_REDISPLAY_P (f) && !FRAME_OBSCURED_P (f)) + if (FRAME_REDISPLAY_P (f)) { /* Don't allow freeing images and faces for this frame as long as the frame's update wasn't @@ -17505,7 +17528,7 @@ #define AINC(a,i) \ if (gcscrollbars && FRAME_TERMINAL (f)->judge_scroll_bars_hook) FRAME_TERMINAL (f)->judge_scroll_bars_hook (f); - if (FRAME_REDISPLAY_P (f) && !FRAME_OBSCURED_P (f)) + if (FRAME_REDISPLAY_P (f)) { /* If fonts changed on visible frame, display again. */ if (f->fonts_changed) @@ -17590,6 +17613,9 @@ #define AINC(a,i) \ } } + if (CONSP (tty_root_frames)) + pending |= combine_updates (tty_root_frames, false, false); + eassert (EQ (XFRAME (selected_frame)->selected_window, selected_window)); if (!pending) @@ -17611,7 +17637,7 @@ #define AINC(a,i) \ } } } - else if (FRAME_REDISPLAY_P (sf) && !FRAME_OBSCURED_P (sf)) + else if (FRAME_REDISPLAY_P (sf)) { sf->inhibit_clear_image_cache = true; displayed_buffer = XBUFFER (XWINDOW (selected_window)->contents); @@ -17662,7 +17688,7 @@ #define AINC(a,i) \ unrequest_sigio (); STOP_POLLING; - if (FRAME_REDISPLAY_P (sf) && !FRAME_OBSCURED_P (sf)) + if (FRAME_REDISPLAY_P (sf)) { if (hscroll_retries <= MAX_HSCROLL_RETRIES && hscroll_windows (selected_window)) @@ -17673,6 +17699,10 @@ #define AINC(a,i) \ XWINDOW (selected_window)->must_be_updated_p = true; pending = update_frame (sf, false, false); + + if (is_tty_frame (sf)) + pending |= combine_updates_for_frame (sf, false, false); + sf->cursor_type_changed = false; sf->inhibit_clear_image_cache = false; } @@ -17685,10 +17715,12 @@ #define AINC(a,i) \ Lisp_Object mini_window = FRAME_MINIBUF_WINDOW (sf); struct frame *mini_frame = XFRAME (WINDOW_FRAME (XWINDOW (mini_window))); - if (mini_frame != sf && FRAME_WINDOW_P (mini_frame)) + if (mini_frame != sf) { XWINDOW (mini_window)->must_be_updated_p = true; pending |= update_frame (mini_frame, false, false); + if (is_tty_frame (mini_frame)) + pending |= combine_updates_for_frame (mini_frame, false, false); mini_frame->cursor_type_changed = false; if (!pending && hscroll_retries <= MAX_HSCROLL_RETRIES && hscroll_windows (mini_window)) @@ -23975,6 +24007,7 @@ extend_face_to_end_of_line (struct it *it) { it->glyph_row->glyphs[TEXT_AREA][0] = space_glyph; it->glyph_row->glyphs[TEXT_AREA][0].face_id = face->id; + it->glyph_row->glyphs[TEXT_AREA][0].frame = f; it->glyph_row->used[TEXT_AREA] = 1; } /* Mode line and the header line don't have margins, and @@ -23994,6 +24027,7 @@ extend_face_to_end_of_line (struct it *it) it->glyph_row->glyphs[LEFT_MARGIN_AREA][0] = space_glyph; it->glyph_row->glyphs[LEFT_MARGIN_AREA][0].face_id = default_face->id; + it->glyph_row->glyphs[LEFT_MARGIN_AREA][0].frame = f; it->glyph_row->used[LEFT_MARGIN_AREA] = 1; } if (WINDOW_RIGHT_MARGIN_WIDTH (it->w) > 0 @@ -24002,6 +24036,7 @@ extend_face_to_end_of_line (struct it *it) it->glyph_row->glyphs[RIGHT_MARGIN_AREA][0] = space_glyph; it->glyph_row->glyphs[RIGHT_MARGIN_AREA][0].face_id = default_face->id; + it->glyph_row->glyphs[RIGHT_MARGIN_AREA][0].frame = f; it->glyph_row->used[RIGHT_MARGIN_AREA] = 1; } @@ -24366,9 +24401,11 @@ highlight_trailing_whitespace (struct it *it) while (glyph >= start && BUFFERP (glyph->object) && (glyph->type == STRETCH_GLYPH - || (glyph->type == CHAR_GLYPH - && glyph->u.ch == ' '))) - (glyph--)->face_id = face_id; + || (glyph->type == CHAR_GLYPH && glyph->u.ch == ' '))) + { + glyph->frame = it->f; + (glyph--)->face_id = face_id; + } } else { @@ -24377,7 +24414,10 @@ highlight_trailing_whitespace (struct it *it) && (glyph->type == STRETCH_GLYPH || (glyph->type == CHAR_GLYPH && glyph->u.ch == ' '))) - (glyph++)->face_id = face_id; + { + glyph->frame = it->f; + (glyph++)->face_id = face_id; + } } } } @@ -27230,7 +27270,7 @@ display_menu_bar (struct window *w) /* Deep copy of a glyph row, including the glyphs. */ static void -deep_copy_glyph_row (struct glyph_row *to, struct glyph_row *from) +deep_copy_glyph_row (struct frame *f, struct glyph_row *to, struct glyph_row *from) { struct glyph *pointers[1 + LAST_AREA]; int to_used = to->used[TEXT_AREA]; @@ -27251,7 +27291,7 @@ deep_copy_glyph_row (struct glyph_row *to, struct glyph_row *from) /* If we filled only part of the TO row, fill the rest with space_glyph (which will display as empty space). */ if (to_used > from->used[TEXT_AREA]) - fill_up_frame_row_with_spaces (to, to_used); + fill_up_frame_row_with_spaces (f, to, to_used); } /* Display one menu item on a TTY, by overwriting the glyphs in the @@ -27300,7 +27340,7 @@ display_tty_menu_item (const char *item_text, int width, int face_id, it.last_visible_x = FRAME_COLS (f) - 1; row = it.glyph_row; /* Start with the row contents from the current matrix. */ - deep_copy_glyph_row (row, f->current_matrix->rows + y); + deep_copy_glyph_row (f, row, f->current_matrix->rows + y); bool saved_width = row->full_width_p; row->full_width_p = true; bool saved_reversed = row->reversed_p; modified src/xfaces.c @@ -696,7 +696,6 @@ init_frame_faces (struct frame *f) free_frame_faces (struct frame *f) { struct face_cache *face_cache = FRAME_FACE_CACHE (f); - if (face_cache) { free_face_cache (face_cache);