commit 12af7ee46d47152a2c499e24970a6ab4a94aed61 (HEAD, refs/remotes/origin/master) Author: Juri Linkov Date: Sun Aug 1 11:38:51 2021 +0300 * lisp/filecache.el: Fix cycling (bug#49761). (file-cache-cycle): Refactor from file-cache-minibuffer-complete. (file-cache-minibuffer-complete): Use file-cache-cycle in 2 old places, and in 1 following new place. When last-command is equal to this-command, use file-cache-cycle to continue cycling the previous completion as long as the user continues typing C-TAB. Also when displaying a list of completions, don't try to move point to the common prefix. diff --git a/etc/NEWS b/etc/NEWS index 78e4e47248..bd5c803d37 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3096,7 +3096,7 @@ ledit.el, lmenu.el, lucid.el and old-whitespace.el. 'erc-announced-server-name', 'erc-default-coding-system', 'erc-process', 'erc-send-command', 'eshell-report-bug', 'eval-next-after-load', 'exchange-dot-and-mark', 'ffap-bug', -'ffap-submit-bug', 'ffap-version', 'file-cache-choose-completion', +'ffap-submit-bug', 'ffap-version', 'file-cache-mouse-choose-completion', 'forward-point', 'generic-char-p', 'global-highlight-changes', 'hi-lock-face-history', 'hi-lock-regexp-history', 'highlight-changes-active-string', 'highlight-changes-initial-state', diff --git a/lisp/filecache.el b/lisp/filecache.el index 62184e1a0a..4223878b0e 100644 --- a/lisp/filecache.el +++ b/lisp/filecache.el @@ -516,6 +516,16 @@ If called interactively, read the directory names one by one." (concat directory "/") directory))) +(defun file-cache-cycle (name) + "Cycle through the directories that NAME is available in." + (let ((file-name (file-cache-file-name name))) + (if (string= file-name (minibuffer-contents)) + (minibuffer-message file-cache-sole-match-message) + (delete-minibuffer-contents) + (insert file-name) + (if file-cache-multiple-directory-message + (minibuffer-message file-cache-multiple-directory-message))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Minibuffer functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -545,13 +555,7 @@ the name is considered already unique; only the second substitution (cond ;; If it's the only match, replace the original contents ((or arg (eq completion t)) - (let ((file-name (file-cache-file-name string))) - (if (string= file-name (minibuffer-contents)) - (minibuffer-message file-cache-sole-match-message) - (delete-minibuffer-contents) - (insert file-name) - (if file-cache-multiple-directory-message - (minibuffer-message file-cache-multiple-directory-message))))) + (file-cache-cycle string)) ;; If it's the longest match, insert it ((consp completion) @@ -564,10 +568,7 @@ the name is considered already unique; only the second substitution file-cache-ignore-case)) (if (and (eq last-command this-command) (string= file-cache-last-completion newstring)) - (progn - (delete-minibuffer-contents) - (insert (file-cache-file-name newstring)) - (setq file-cache-last-completion nil)) + (file-cache-cycle newstring) (minibuffer-message file-cache-non-unique-message) (setq file-cache-last-completion string)) (setq file-cache-last-completion string) @@ -579,20 +580,12 @@ the name is considered already unique; only the second substitution (if (> (length completion-list) 1) (progn (delete-region (- (point-max) (length string)) (point-max)) - (save-excursion (insert newstring)) - (forward-char newpoint) + (insert newstring) (with-output-to-temp-buffer file-cache-completions-buffer (display-completion-list completion-list) ;; Add our own setup function to the Completions Buffer (file-cache-completion-setup-function))) - (let ((file-name (file-cache-file-name newstring))) - (if (string= file-name (minibuffer-contents)) - (minibuffer-message file-cache-sole-match-message) - (delete-minibuffer-contents) - (insert file-name) - (if file-cache-multiple-directory-message - (minibuffer-message - file-cache-multiple-directory-message))))))))) + (file-cache-cycle newstring)))))) ;; No match ((eq completion nil) commit 7640f1da0be206a7598c96acdfdaaf390a2b546c Author: Kévin Le Gouguec Date: Sat Jul 31 23:54:06 2021 +0200 Extend Gnus summary highlight faces by default * lisp/gnus/gnus.el (gnus-summary-selected): (gnus-summary-normal-ancient): (gnus-summary-normal-undownloaded): (gnus-summary-normal-unread): (gnus-summary-normal-read): Set :extend attribute. diff --git a/lisp/gnus/gnus.el b/lisp/gnus/gnus.el index 7dde799a5b..8b93acccca 100644 --- a/lisp/gnus/gnus.el +++ b/lisp/gnus/gnus.el @@ -525,25 +525,26 @@ be set in `.emacs' instead." ;; Summary mode faces. -(defface gnus-summary-selected '((t (:underline t))) +(defface gnus-summary-selected '((t (:underline t :extend t))) "Face used for selected articles." :group 'gnus-summary) (defface gnus-summary-cancelled '((((class color)) - (:foreground "yellow" :background "black"))) + (:foreground "yellow" :background "black" :extend t)) + (t (:extend t))) "Face used for canceled articles." :group 'gnus-summary) (defface gnus-summary-normal-ticked '((((class color) (background dark)) - (:foreground "pink")) + (:foreground "pink" :extend t)) (((class color) (background light)) - (:foreground "firebrick")) + (:foreground "firebrick" :extend t)) (t - ())) + (:extend t))) "Face used for normal interest ticked articles." :group 'gnus-summary) @@ -560,12 +561,12 @@ be set in `.emacs' instead." (defface gnus-summary-normal-ancient '((((class color) (background dark)) - (:foreground "SkyBlue")) + (:foreground "SkyBlue" :extend t)) (((class color) (background light)) - (:foreground "RoyalBlue")) + (:foreground "RoyalBlue" :extend t)) (t - ())) + (:extend t))) "Face used for normal interest ancient articles." :group 'gnus-summary) @@ -582,10 +583,10 @@ be set in `.emacs' instead." (defface gnus-summary-normal-undownloaded '((((class color) (background light)) - (:foreground "cyan4" :bold nil)) + (:foreground "cyan4" :bold nil :extend t)) (((class color) (background dark)) - (:foreground "LightGray" :bold nil)) - (t (:inverse-video t))) + (:foreground "LightGray" :bold nil :extend t)) + (t (:inverse-video t :extend t))) "Face used for normal interest uncached articles." :group 'gnus-summary) @@ -601,7 +602,7 @@ be set in `.emacs' instead." (defface gnus-summary-normal-unread '((t - ())) + (:extend t))) "Face used for normal interest unread articles." :group 'gnus-summary) @@ -618,12 +619,12 @@ be set in `.emacs' instead." (defface gnus-summary-normal-read '((((class color) (background dark)) - (:foreground "PaleGreen")) + (:foreground "PaleGreen" :extend t)) (((class color) (background light)) - (:foreground "DarkGreen")) + (:foreground "DarkGreen" :extend t)) (t - ())) + (:extend t))) "Face used for normal interest read articles." :group 'gnus-summary) commit 2a2e7e5466ed229feb2b4cc062c2519cd51f731f Author: Lars Ingebrigtsen Date: Sun Aug 1 00:01:59 2021 +0200 Adjust the fully qualified host name when nothing is set * doc/misc/message.texi (News Headers): Adjust index. * lisp/gnus/message.el (message-check-news-header-syntax): Adjust check. (message-make-fqdn): Be less hilarious. diff --git a/doc/misc/message.texi b/doc/misc/message.texi index d2353e6cec..c0e3dfae12 100644 --- a/doc/misc/message.texi +++ b/doc/misc/message.texi @@ -2084,7 +2084,7 @@ This optional header will be computed by Message. @vindex user-mail-address @findex system-name @cindex Sun -@cindex i-did-not-set--mail-host-address--so-tickle-me +@cindex mail-host-address-is-not-set This required header will be generated by Message. A unique ID will be created based on the date, time, user name (for the local part) and the domain part. For the domain part, message will look (in this order) at diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el index 9baf09b026..bcbf747671 100644 --- a/lisp/gnus/message.el +++ b/lisp/gnus/message.el @@ -5357,7 +5357,7 @@ Otherwise, generate and save a value for `canlock-password' first." ;; Check "Shoot me". (message-check 'shoot (if (re-search-forward - "Message-ID.*.i-did-not-set--mail-host-address--so-tickle-me" nil t) + "Message-ID.*.mail-host-address-is-not-set" nil t) (y-or-n-p "You appear to have a misconfigured system. Really post? ") t)) ;; Check for Approved. @@ -6068,8 +6068,7 @@ give as trustworthy answer as possible." user-domain) ;; Default to this bogus thing. (t - (concat sysname - ".i-did-not-set--mail-host-address--so-tickle-me"))))) + (concat sysname ".mail-host-address-is-not-set"))))) (defun message-make-domain () "Return the domain name." commit 4ca3cf996e4a2c278322091d7d17e7b0d935aa4d Author: Lars Ingebrigtsen Date: Sat Jul 31 23:49:00 2021 +0200 Fix `u' binding in Gnus Browse mode * lisp/gnus/gnus-srvr.el (gnus-browse-mode-map): Bind to `gnus-browse-toggle-subscription-at-point', which is the command. diff --git a/lisp/gnus/gnus-srvr.el b/lisp/gnus/gnus-srvr.el index b316a2e86f..1c75abb6f4 100644 --- a/lisp/gnus/gnus-srvr.el +++ b/lisp/gnus/gnus-srvr.el @@ -716,7 +716,7 @@ claim them." "\M-n" gnus-browse-next-group "\M-p" gnus-browse-prev-group "\r" gnus-browse-select-group - "u" gnus-browse-toggle-subscription + "u" gnus-browse-toggle-subscription-at-point "l" gnus-browse-exit "L" gnus-browse-exit "q" gnus-browse-exit @@ -735,7 +735,7 @@ claim them." (easy-menu-define gnus-browse-menu gnus-browse-mode-map "" '("Browse" - ["Toggle Subscribe" gnus-browse-toggle-subscription t] + ["Toggle Subscribe" gnus-browse-toggle-subscription-at-point t] ["Read" gnus-browse-read-group t] ["Select" gnus-browse-select-group t] ["Describe" gnus-browse-describe-group t] @@ -881,7 +881,7 @@ All normal editing commands are switched off. \\ The only things you can do in this buffer is -1) `\\[gnus-browse-toggle-subscription]' to subscribe or unsubscribe to +1) `\\[gnus-browse-toggle-subscription-at-point]' to subscribe or unsubscribe to a group. The group will be inserted into the group buffer upon exit from this buffer. commit 5f33ad6954656096f53dc1ed2e941db37b5a3fc1 Author: Mattias Engdegård Date: Sat Jul 31 23:44:17 2021 +0200 ; compilation-error-regexp-alist-alist): eval-when-compile diff --git a/lisp/progmodes/compile.el b/lisp/progmodes/compile.el index 02d1c58858..1fb6124ab5 100644 --- a/lisp/progmodes/compile.el +++ b/lisp/progmodes/compile.el @@ -173,6 +173,7 @@ and a string describing how the process finished.") ;; emacs -batch -l compile-tests.el -f ert-run-tests-batch-and-exit (defvar compilation-error-regexp-alist-alist + (eval-when-compile `((absoft "^\\(?:[Ee]rror on \\|[Ww]arning on\\( \\)\\)?[Ll]ine[ \t]+\\([0-9]+\\)[ \t]+\ of[ \t]+\"?\\([a-zA-Z]?:?[^\":\n]+\\)\"?:" 3 2 nil (1)) @@ -615,7 +616,7 @@ File = \\(.+\\), Line = \\([0-9]+\\)\\(?:, Column = \\([0-9]+\\)\\)?" ;; we do not know what lines will follow. (guile-file "^In \\(.+\\..+\\):\n" 1 nil nil 0) (guile-line "^ *\\([0-9]+\\): *\\([0-9]+\\)" nil 1 2) - ) + )) "Alist of values for `compilation-error-regexp-alist'.") (defcustom compilation-error-regexp-alist commit 6413f08887ccb4fa8049f4665bba4f77368f15c6 Author: Mattias Engdegård Date: Sat Jul 31 22:37:03 2021 +0200 Occur-mode multi-line match property gap filling When an occur-mode regexp matches across multiple lines, the spacing prefixes inserted between each did not have the `occur-target` property which is essential for jumping to the corresponding place in the target buffer. This prevented next-error and previous-error from working. * lisp/replace.el (occur-engine): Put the `occur-target` property on the continuation prefix to avoid the gap. diff --git a/lisp/replace.el b/lisp/replace.el index d0c6366915..148b7ce48b 100644 --- a/lisp/replace.el +++ b/lisp/replace.el @@ -2089,8 +2089,10 @@ See also `multi-occur'." "\n" (if prefix-face (propertize - "\n :" 'font-lock-face prefix-face) - "\n :") + "\n :" 'font-lock-face prefix-face + 'occur-target markers) + (propertize + "\n :" 'occur-target markers)) ;; Add mouse face in one section to ;; ensure the prefix and the string ;; get a contiguous highlight. commit ff16bea7f478be80f37c4870e69d43825d4f8cd6 Author: Mattias Engdegård Date: Sat Jul 31 11:22:37 2021 +0200 * etc/NEWS: Mention occur-mode highlight changes. diff --git a/etc/NEWS b/etc/NEWS index 1ab0dbce1a..78e4e47248 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -787,6 +787,10 @@ faces in other ways. *** The new command 'recenter-current-error', bound to 'l' in Occur or compilation buffers, recenters the current displayed occurrence/error. +*** Matches in target buffers are now highlighted as in 'compilation-mode'. +The method of highlighting is specified by the user options +'next-error-highlight' and 'next-error-highlight-no-select'. + --- *** Occur mode may use a different type for 'occur-target' property values. The value was previously always a marker set to the start of the first commit 943d79914be22bb2e43a4b3fa9a98b43093424fd Author: Lars Ingebrigtsen Date: Sat Jul 31 22:36:19 2021 +0200 C-x 5 5 manual tweak * doc/emacs/frames.texi (Creating Frames): Mention other-frame-prefix function name. diff --git a/doc/emacs/frames.texi b/doc/emacs/frames.texi index 70615f68ed..951e0902cb 100644 --- a/doc/emacs/frames.texi +++ b/doc/emacs/frames.texi @@ -480,9 +480,10 @@ frame. This runs @code{find-file-read-only-other-frame}. @xref{Visiting}. @item C-x 5 5 -A more general prefix command affects the buffer displayed by the next -command invoked immediately after this prefix command. It requests -the buffer of the next command to be displayed in another frame. +A more general prefix command that affects the buffer displayed by the +next command invoked immediately after this prefix command +(@code{other-frame-prefix}). It requests the buffer of the next +command to be displayed in another frame. @end table You can control the appearance and behavior of the newly-created diff --git a/etc/NEWS b/etc/NEWS index bc9fbb3aa7..1ab0dbce1a 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -601,8 +601,10 @@ This is used to fontify non-scalar variables. This allows the user to easier customize whether to use block-based navigation or not. +--- *** 'python-shell-interpreter' now defaults to python3 on systems with python3. +--- *** 'C-c C-r' can now be used on arbitrary regions. The command previously extended the start of the region to the start of the line, but will now actually send the marked region, as @@ -610,6 +612,7 @@ documented. ** Ruby mode +--- *** 'ruby-use-smie' is declared obsolete. SMIE is now always enabled and 'ruby-use-smie' only controls whether indentation is done using SMIE or with the old ad-hoc code. @@ -630,10 +633,13 @@ Icomplete, completions are rotated and selection kept at the top. When used with Fido, completions scroll like a typical dropdown widget. +--- *** Default value of 'icomplete-compute-delay' has been changed to 0.15 s. +--- *** Default value of 'icomplete-max-delay-chars' has been changed to 2. +--- *** Reduced blinking while completing the next completions set. Icomplete doesn't hide the hint with the previously computed completions anymore when compute delay is in effect, or the previous @@ -655,6 +661,7 @@ disabled entirely. ** Windmove ++++ *** New user options to customize windmove keybindings. These options include 'windmove-default-keybindings', 'windmove-display-default-keybindings', @@ -693,6 +700,7 @@ of the next command to be displayed in a new window. ** Frames ++++ *** The key prefix 'C-x 5 5' displays next command buffer in a new frame. It's bound to the command 'other-frame-prefix' that requests the buffer of the next command to be displayed in a new frame. commit fa09452ab5fab975c57f003827dfa45c537b3484 Author: Lars Ingebrigtsen Date: Sat Jul 31 22:27:08 2021 +0200 Update NEWS tagging for a couple entries diff --git a/etc/NEWS b/etc/NEWS index 3cda75d804..bc9fbb3aa7 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -488,12 +488,14 @@ Typing 'TAB' on a heading line cycles the current section between heading line cycles the whole buffer between "only top-level headings", "all headings and subheadings", and "show all" states. +--- *** New user option 'outline-minor-mode-highlight'. This user option customizes 'outline-minor-mode'. It puts highlighting on heading lines using standard outline faces. This works well only when there are no conflicts with faces used by the major mode. ++++ ** New commands 'copy-matching-lines' and 'kill-matching-lines'. These commands are similar to the command 'flush-lines', but add the matching lines to the kill ring as a single string, commit 3019d628e2a07c7be79ee8ae878c55f836d9989f Author: Lars Ingebrigtsen Date: Sat Jul 31 22:25:38 2021 +0200 Add an index entry for outline-minor-mode-cycle * doc/emacs/text.texi (Outline Mode): Document outline-minor-mode-cycle. diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi index 1fe278131f..3b9d5c2ea6 100644 --- a/doc/emacs/text.texi +++ b/doc/emacs/text.texi @@ -997,6 +997,7 @@ specific file (@pxref{File Variables}). major mode's special commands. (The variable @code{outline-minor-mode-prefix} controls the prefix used.) +@vindex outline-minor-mode-cycle If the @code{outline-minor-mode-cycle} user option is non-@code{nil}, the @kbd{TAB} and @kbd{S-TAB} keys are enabled on the outline heading lines. @kbd{TAB} cycles hiding, showing the commit 59f80ffc7daa15802bf8b010543277f60163f04f Author: Lars Ingebrigtsen Date: Sat Jul 31 22:22:42 2021 +0200 Document outline-minor-mode-cycle in the manual. * doc/emacs/text.texi (Outline Mode): Document outline-minor-mode-cycle. diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi index c9c4be3c61..1fe278131f 100644 --- a/doc/emacs/text.texi +++ b/doc/emacs/text.texi @@ -997,6 +997,12 @@ specific file (@pxref{File Variables}). major mode's special commands. (The variable @code{outline-minor-mode-prefix} controls the prefix used.) + If the @code{outline-minor-mode-cycle} user option is +non-@code{nil}, the @kbd{TAB} and @kbd{S-TAB} keys are enabled on the +outline heading lines. @kbd{TAB} cycles hiding, showing the +sub-heading, and showing all for the current section. @kbd{S-TAB} +does the same for the entire buffer. + @menu * Outline Format:: What the text of an outline looks like. * Outline Motion:: Special commands for moving through outlines. diff --git a/etc/NEWS b/etc/NEWS index 1a7f20bb8b..3cda75d804 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -479,6 +479,7 @@ Typing 'TAB' on a heading line cycles the current section between anywhere in the buffer cycles the whole buffer between "only top-level headings", "all headings and subheadings", and "show all" states. ++++ *** New user option 'outline-minor-mode-cycle'. This user option customizes 'outline-minor-mode', with the difference that 'TAB' and 'S-TAB' on heading lines cycle heading visibility. commit d3d6ea50d2677ea939c4d05e1dc2d95cb0c42be4 Author: Lars Ingebrigtsen Date: Sat Jul 31 19:51:16 2021 +0200 Make `ffap-read-file-or-url' work again (to read URLs) * lisp/ffap.el (ffap--url-file-handler): New function (bug#44822). (ffap-read-file-or-url): Use it to allow switching between URLs and files. diff --git a/lisp/ffap.el b/lisp/ffap.el index c31926eb29..b398d1c9f2 100644 --- a/lisp/ffap.el +++ b/lisp/ffap.el @@ -1525,24 +1525,40 @@ which may actually result in an URL rather than a filename." ;; The solution here is to forcefully activate url-handler-mode, which ;; takes care of it for us. +(defun ffap--url-file-handler (operation &rest args) + (let ((inhibit-file-name-handlers + (cons 'ffap--url-file-handler inhibit-file-name-handlers)) + (inhibit-file-name-operation operation)) + (cl-case operation + ;; We mainly just want to disable these bits: + (substitute-in-file-name (car args)) + (expand-file-name + (if (equal (car args) "http://") + "" + (car args))) + (otherwise + (apply operation args))))) + (defun ffap-read-file-or-url (prompt guess) "Read file or URL from minibuffer, with PROMPT and initial GUESS." - (or guess (setq guess default-directory)) - ;; Tricky: guess may have or be a local directory, like "w3/w3.elc" - ;; or "w3/" or "../el/ffap.el" or "../../../" - (if (ffap-url-p guess) - ;; FIXME: We earlier tried to make use of `url-file-handler' so - ;; `read-file-name' could also be used for URLs, but it - ;; introduced all kinds of subtle breakage such as: - ;; - (file-name-directory "http://a") returning "http://a/" - ;; - Trying to contact remote hosts with no justification - ;; These should be fixed in url-handler-mode before we can try - ;; using it here again. - (read-string prompt guess nil nil t) - (unless (ffap-file-remote-p guess) - (setq guess (abbreviate-file-name (expand-file-name guess)))) - (read-file-name prompt (file-name-directory guess) nil nil - (file-name-nondirectory guess)))) + (let ((elem (cons ffap-url-regexp #'ffap--url-file-handler))) + (unwind-protect + (progn + (push elem file-name-handler-alist) + (if (ffap-url-p guess) + (read-file-name prompt "http://" nil nil guess) + (unless guess + (setq guess default-directory)) + (unless (ffap-file-remote-p guess) + (setq guess (abbreviate-file-name (expand-file-name guess)))) + (read-file-name prompt + (file-name-directory guess) nil nil + (file-name-nondirectory guess)))) + ;; Remove the special handler manually. We used to just let-bind + ;; file-name-handler-alist to preserve its value, but that caused + ;; other modifications to be lost (e.g. when Tramp gets loaded + ;; during the completing-read call). + (setq file-name-handler-alist (delq elem file-name-handler-alist))))) ;; The rest of this page is just to work with package complete.el. ;; This code assumes that you load ffap.el after complete.el. commit 2dcb0f8f5241d0fcdcdb3da10d3eb4d7916e4869 Author: Lars Ingebrigtsen Date: Sat Jul 31 18:44:02 2021 +0200 Add new user option python-forward-sexp-function * lisp/progmodes/python.el (python-forward-sexp-function): New user option (bug#41361). (python-mode): Use it. diff --git a/etc/NEWS b/etc/NEWS index 40a5e512fb..1a7f20bb8b 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -593,6 +593,11 @@ This is used to fontify non-scalar variables. ** Python mode +--- +*** New user option 'python-forward-sexp-function'. +This allows the user to easier customize whether to use block-based +navigation or not. + *** 'python-shell-interpreter' now defaults to python3 on systems with python3. *** 'C-c C-r' can now be used on arbitrary regions. diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index f7267bdef2..2557704e40 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -54,14 +54,7 @@ ;; `python-nav-backward-statement', ;; `python-nav-beginning-of-statement', `python-nav-end-of-statement', ;; `python-nav-beginning-of-block', `python-nav-end-of-block' and -;; `python-nav-if-name-main' are included but no bound to any key. At -;; last but not least the specialized `python-nav-forward-sexp' allows -;; easy navigation between code blocks. If you prefer `cc-mode'-like -;; `forward-sexp' movement, setting `forward-sexp-function' to nil is -;; enough, You can do that using the `python-mode-hook': - -;; (add-hook 'python-mode-hook -;; (lambda () (setq forward-sexp-function nil))) +;; `python-nav-if-name-main' are included but no bound to any key. ;; Shell interaction: is provided and allows opening Python shells ;; inside Emacs and executing any block of code of your current buffer @@ -5505,6 +5498,13 @@ By default messages are considered errors." :type '(alist :key-type (regexp) :value-type (symbol))) +(defcustom python-forward-sexp-function #'python-nav-forward-sexp + "Function to use when navigating between expressions." + :version "28.1" + :type '(choice (const :tag "Python blocks" python-nav-forward-sexp) + (const :tag "CC-mode like" nil) + function)) + (defvar-local python--flymake-proc nil) (defun python--flymake-parse-output (source proc report-fn) @@ -5602,7 +5602,7 @@ REPORT-FN is Flymake's callback function." (setq-local parse-sexp-lookup-properties t) (setq-local parse-sexp-ignore-comments t) - (setq-local forward-sexp-function #'python-nav-forward-sexp) + (setq-local forward-sexp-function python-forward-sexp-function) (setq-local font-lock-defaults `(,python-font-lock-keywords commit f6c5a801efdad6cc7ed16ac0b1fa53599b84bc91 Author: Lars Ingebrigtsen Date: Sat Jul 31 17:44:43 2021 +0200 Adjust how `replace-match' runs modification hooks * src/editfns.c (Fsubst_char_in_region) (Ftranslate_region_internal): * src/cmds.c (internal_self_insert): Update callers. * src/insdel.c (replace_range): Allow inhibiting signal_after_change/update_compositions. * src/lisp.h: Update. * src/search.c (Freplace_match): Run the modification hooks at the end instead of before adjusting point (bug#42424). diff --git a/etc/NEWS b/etc/NEWS index ef115d0ae0..40a5e512fb 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2920,6 +2920,15 @@ This is to keep the same behavior as Eshell. * Incompatible Lisp Changes in Emacs 28.1 +--- +** 'replace-match' now runs modification hooks slightly later. +The function is documented to leave point after the replacement text, +but this was not always the case if a modification hook inserted text +in front of the replaced text -- 'replace-match' would instead leave +point where the end of the inserted text would have been before the +hook ran. 'replace-match' now always leaves point after the +replacement text. + --- ** 'kill-all-local-variables' has changed how it handles non-symbol hooks. The function is documented to eliminate all buffer-local bindings diff --git a/src/cmds.c b/src/cmds.c index c8a96d918c..00fde0ef79 100644 --- a/src/cmds.c +++ b/src/cmds.c @@ -455,7 +455,7 @@ internal_self_insert (int c, EMACS_INT n) ptrdiff_t to; if (INT_ADD_WRAPV (PT, chars_to_delete, &to)) to = PTRDIFF_MAX; - replace_range (PT, to, string, 1, 1, 1, 0); + replace_range (PT, to, string, 1, 1, 1, 0, false); Fforward_char (make_fixnum (n)); } else if (n > 1) diff --git a/src/editfns.c b/src/editfns.c index 8ab17ebc9f..c8219decb0 100644 --- a/src/editfns.c +++ b/src/editfns.c @@ -2371,7 +2371,7 @@ Both characters must have the same length of multi-byte form. */) /* replace_range is less efficient, because it moves the gap, but it handles combining correctly. */ replace_range (pos, pos + 1, string, - false, false, true, false); + false, false, true, false, false); pos_byte_next = CHAR_TO_BYTE (pos); if (pos_byte_next > pos_byte) /* Before combining happened. We should not increment @@ -2578,7 +2578,7 @@ It returns the number of characters changed. */) but it should handle multibyte characters correctly. */ string = make_multibyte_string ((char *) str, 1, str_len); replace_range (pos, pos + 1, string, - true, false, true, false); + true, false, true, false, false); len = str_len; } else @@ -2613,7 +2613,8 @@ It returns the number of characters changed. */) = (VECTORP (val) ? Fconcat (1, &val) : Fmake_string (make_fixnum (1), val, Qnil)); - replace_range (pos, pos + len, string, true, false, true, false); + replace_range (pos, pos + len, string, true, false, true, false, + false); pos_byte += SBYTES (string); pos += SCHARS (string); characters_changed += SCHARS (string); diff --git a/src/insdel.c b/src/insdel.c index e66120eb08..40674e15e4 100644 --- a/src/insdel.c +++ b/src/insdel.c @@ -1392,7 +1392,7 @@ adjust_after_insert (ptrdiff_t from, ptrdiff_t from_byte, void replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new, bool prepare, bool inherit, bool markers, - bool adjust_match_data) + bool adjust_match_data, bool inhibit_mod_hooks) { ptrdiff_t inschars = SCHARS (new); ptrdiff_t insbytes = SBYTES (new); @@ -1552,8 +1552,11 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new, if (adjust_match_data) update_search_regs (from, to, from + SCHARS (new)); - signal_after_change (from, nchars_del, GPT - from); - update_compositions (from, GPT, CHECK_BORDER); + if (!inhibit_mod_hooks) + { + signal_after_change (from, nchars_del, GPT - from); + update_compositions (from, GPT, CHECK_BORDER); + } } /* Replace the text from character positions FROM to TO with diff --git a/src/lisp.h b/src/lisp.h index 15a42a4456..1206a0d1f6 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -3717,7 +3717,8 @@ extern void adjust_markers_for_delete (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t); extern void adjust_markers_bytepos (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, int); -extern void replace_range (ptrdiff_t, ptrdiff_t, Lisp_Object, bool, bool, bool, bool); +extern void replace_range (ptrdiff_t, ptrdiff_t, Lisp_Object, bool, bool, + bool, bool, bool); extern void replace_range_2 (ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t, const char *, ptrdiff_t, ptrdiff_t, bool); extern void syms_of_insdel (void); diff --git a/src/search.c b/src/search.c index df384e1dcf..14adeb58e9 100644 --- a/src/search.c +++ b/src/search.c @@ -30,6 +30,7 @@ along with GNU Emacs. If not, see . */ #include "blockinput.h" #include "intervals.h" #include "pdumper.h" +#include "composite.h" #include "regex-emacs.h" @@ -2725,7 +2726,7 @@ since only regular expressions have distinguished subexpressions. */) newpoint = sub_start + SCHARS (newtext); /* Replace the old text with the new in the cleanest possible way. */ - replace_range (sub_start, sub_end, newtext, 1, 0, 1, true); + replace_range (sub_start, sub_end, newtext, 1, 0, 1, true, true); if (case_action == all_caps) Fupcase_region (make_fixnum (search_regs.start[sub]), @@ -2750,6 +2751,9 @@ since only regular expressions have distinguished subexpressions. */) /* Now move point "officially" to the end of the inserted replacement. */ move_if_not_intangible (newpoint); + signal_after_change (sub_start, sub_end - sub_start, SCHARS (newtext)); + update_compositions (sub_start, newpoint, CHECK_BORDER); + return Qnil; } diff --git a/test/src/search-tests.el b/test/src/search-tests.el new file mode 100644 index 0000000000..b7b4ab9a8f --- /dev/null +++ b/test/src/search-tests.el @@ -0,0 +1,42 @@ +;;; search-tests.el --- tests for search.c functions -*- lexical-binding: t -*- + +;; Copyright (C) 2015-2016, 2018-2021 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 . + +;;; Code: + +(require 'ert) + +(ert-deftest test-replace-match-modification-hooks () + (let ((ov-set nil)) + (with-temp-buffer + (insert "1 abc") + (setq ov-set (make-overlay 3 5)) + (overlay-put + ov-set 'modification-hooks + (list (lambda (o after &rest _args) + (when after + (let ((inhibit-modification-hooks t)) + (save-excursion + (goto-char 2) + (insert "234"))))))) + (goto-char 3) + (if (search-forward "bc") + (replace-match "bcd")) + (should (= (point) 10))))) + +;;; search-tests.el ends here commit c4239ec32c944e74252937bfc52e341f81b7a5a4 Author: Lars Ingebrigtsen Date: Sat Jul 31 16:52:44 2021 +0200 Extend whitespace-empty to the end of the line * lisp/whitespace.el (whitespace-empty): Restore Emacs 26 look by extending the face (bug#42112). diff --git a/lisp/whitespace.el b/lisp/whitespace.el index aaa56835cd..a2dc6ab981 100644 --- a/lisp/whitespace.el +++ b/lisp/whitespace.el @@ -593,7 +593,7 @@ Used when `whitespace-style' includes the value `empty'.") (defface whitespace-empty '((((class mono)) :inverse-video t :weight bold :underline t) - (t :background "yellow" :foreground "firebrick")) + (t :background "yellow" :foreground "firebrick" :extend t)) "Face used to visualize empty lines at beginning and/or end of buffer." :group 'whitespace) commit 5ecf39a5eab1c697b5d15c3b5df230bf7d1a11b2 Author: Lars Ingebrigtsen Date: Sat Jul 31 14:54:52 2021 +0200 Revert "Fix `speedbar-directory-buttons' when using Tramp" This reverts commit 5afad3918bc8816b74e8efcff9cc441785446aa6. This patch can't possibly be correct, and it breaks the stated interface. diff --git a/lisp/speedbar.el b/lisp/speedbar.el index 4666026f35..34fbec9c21 100644 --- a/lisp/speedbar.el +++ b/lisp/speedbar.el @@ -1822,9 +1822,9 @@ matches the user directory ~, then it is replaced with a ~. INDEX is not used, but is required by the caller." (let* ((tilde (expand-file-name "~/")) (dd (expand-file-name directory)) - (junk (string-prefix-p "~/" dd)) + (junk (string-match (regexp-quote tilde) dd)) (displayme (if junk - (concat "~/" (substring dd 2 nil)) + (concat "~/" (substring dd (match-end 0))) dd)) (p (point))) (if (string-match "^~[/\\]?\\'" displayme) (setq displayme tilde)) commit 32b9c7d06f774b420b5d2cfbbdbb447f542fc88e Author: Lars Ingebrigtsen Date: Sat Jul 31 13:37:42 2021 +0200 Revert "Allow nil initializers in define-minor-mode" This reverts commit 02cbb37de73d563149389615ee44741322007108. This was mistakenly commited and doesn't really make much sense. diff --git a/lisp/emacs-lisp/easy-mmode.el b/lisp/emacs-lisp/easy-mmode.el index 6530d06b91..3a00fdb454 100644 --- a/lisp/emacs-lisp/easy-mmode.el +++ b/lisp/emacs-lisp/easy-mmode.el @@ -251,9 +251,7 @@ INIT-VALUE LIGHTER KEYMAP. (setq getter `(default-value ',mode)))) (:extra-args (setq extra-args (pop body))) (:set (setq set (list :set (pop body)))) - (:initialize - (when-let ((val (pop body))) - (setq initialize (list :initialize val)))) + (:initialize (setq initialize (list :initialize (pop body)))) (:type (setq type (list :type (pop body)))) (:keymap (setq keymap (pop body))) (:interactive (setq interactive (pop body))) commit 99600ee7b4f7a6b8ab5a25817de21bfadd31c14c Author: Lars Ingebrigtsen Date: Sat Jul 31 13:26:26 2021 +0200 Don't bind and RET in *Help* buffers * lisp/help-mode.el (help-mode-map): Remove key bindings for RET and (bug#49784). (help-xref-stack, help-xref-forward-stack, help-xref-stack-item) (help-make-xrefs): Fix doc strings -- these aren't used by `help-follow', but by `help-follow-symbol'. (help-follow-mouse, help-follow): Make obsolete. diff --git a/lisp/help-mode.el b/lisp/help-mode.el index 3976a9ac4e..8206115b15 100644 --- a/lisp/help-mode.el +++ b/lisp/help-mode.el @@ -35,7 +35,6 @@ (let ((map (make-sparse-keymap))) (set-keymap-parent map (make-composed-keymap button-buffer-map special-mode-map)) - (define-key map [mouse-2] 'help-follow-mouse) (define-key map "l" 'help-go-back) (define-key map "r" 'help-go-forward) (define-key map "\C-c\C-b" 'help-go-back) @@ -43,7 +42,6 @@ (define-key map [XF86Back] 'help-go-back) (define-key map [XF86Forward] 'help-go-forward) (define-key map "\C-c\C-c" 'help-follow-symbol) - (define-key map "\r" 'help-follow) (define-key map "s" 'help-view-source) (define-key map "i" 'help-goto-info) (define-key map "c" 'help-customize) @@ -88,20 +86,20 @@ (defvar-local help-xref-stack nil "A stack of ways by which to return to help buffers after following xrefs. -Used by `help-follow' and `help-xref-go-back'. +Used by `help-follow-symbol' and `help-xref-go-back'. An element looks like (POSITION FUNCTION ARGS...). To use the element, do (apply FUNCTION ARGS) then goto the point.") (put 'help-xref-stack 'permanent-local t) (defvar-local help-xref-forward-stack nil "A stack used to navigate help forwards after using the back button. -Used by `help-follow' and `help-xref-go-forward'. +Used by `help-follow-symbol' and `help-xref-go-forward'. An element looks like (POSITION FUNCTION ARGS...). To use the element, do (apply FUNCTION ARGS) then goto the point.") (put 'help-xref-forward-stack 'permanent-local t) (defvar-local help-xref-stack-item nil - "An item for `help-follow' in this buffer to push onto `help-xref-stack'. + "An item for `help-follow-symbok' to push onto `help-xref-stack'. The format is (FUNCTION ARGS...).") (put 'help-xref-stack-item 'permanent-local t) @@ -466,7 +464,7 @@ Each element has the form (NAME TESTFUN DESCFUN) where: "Parse and hyperlink documentation cross-references in the given BUFFER. Find cross-reference information in a buffer and activate such cross -references for selection with `help-follow'. Cross-references have +references for selection with `help-follow-symbol'. Cross-references have the canonical form `...' and the type of reference may be disambiguated by the preceding word(s) used in `help-xref-symbol-regexp'. Faces only get cross-referenced if @@ -774,6 +772,7 @@ a proper [back] button." ;; The doc string is meant to explain what buttons do. (defun help-follow-mouse () "Follow the cross-reference that you click on." + (declare (obsolete nil "28.1")) (interactive) (error "No cross-reference here")) @@ -782,6 +781,7 @@ a proper [back] button." "Follow cross-reference at point. For the cross-reference format, see `help-make-xrefs'." + (declare (obsolete nil "28.1")) (interactive) (user-error "No cross-reference here")) commit 02cbb37de73d563149389615ee44741322007108 Author: Lars Ingebrigtsen Date: Fri Jul 30 18:25:12 2021 +0200 Allow nil initializers in define-minor-mode * lisp/emacs-lisp/easy-mmode.el (define-minor-mode): Make the meaning of :initialize nil and a missing :initialize the same. diff --git a/lisp/emacs-lisp/easy-mmode.el b/lisp/emacs-lisp/easy-mmode.el index 3a00fdb454..6530d06b91 100644 --- a/lisp/emacs-lisp/easy-mmode.el +++ b/lisp/emacs-lisp/easy-mmode.el @@ -251,7 +251,9 @@ INIT-VALUE LIGHTER KEYMAP. (setq getter `(default-value ',mode)))) (:extra-args (setq extra-args (pop body))) (:set (setq set (list :set (pop body)))) - (:initialize (setq initialize (list :initialize (pop body)))) + (:initialize + (when-let ((val (pop body))) + (setq initialize (list :initialize val)))) (:type (setq type (list :type (pop body)))) (:keymap (setq keymap (pop body))) (:interactive (setq interactive (pop body))) commit 12c5ca4d825496b3c7304b75ab82a6fabdc2023d Author: Alan Third Date: Wed Jun 23 16:07:12 2021 +0100 Fix some macOS problems * src/nsmenu.m (update_frame_tool_bar): Make sure the toolbar isn't displayed when it's not supposed to be. * src/nsterm.m ([EmacsView layoutSublayersOfLayer:]): Reinstate code intended to prevent a crash when running redisplay. diff --git a/src/nsmenu.m b/src/nsmenu.m index 673c0423d0..bb0dd2634d 100644 --- a/src/nsmenu.m +++ b/src/nsmenu.m @@ -1089,10 +1089,10 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item #undef TOOLPROP } - if (![toolbar isVisible]) + if ([toolbar isVisible] != FRAME_EXTERNAL_TOOL_BAR (f)) { f->output_data.ns->in_animation = 1; - [toolbar setVisible: YES]; + [toolbar setVisible: FRAME_EXTERNAL_TOOL_BAR (f)]; f->output_data.ns->in_animation = 0; } diff --git a/src/nsterm.m b/src/nsterm.m index 29a86e4148..3676418c9b 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -7945,20 +7945,15 @@ - (void)layoutSublayersOfLayer:(CALayer *)layer crashes. I think it's because this code will always be run within the run loop and for whatever reason processing input is dangerous. This technique was stolen wholesale from - nsmenu.m and seems to work. - - FIXME: I can't provoke a crash using layoutSublayersOfLayer, - however I can't understand why it would be different from - viewWillDraw. I'll leave this commented out for now, but if - nobody reports a crash it can be removed. */ - // bool owfi = waiting_for_input; - // waiting_for_input = 0; - // block_input (); + nsmenu.m and seems to work. */ + bool owfi = waiting_for_input; + waiting_for_input = 0; + block_input (); redisplay (); - // unblock_input (); - // waiting_for_input = owfi; + unblock_input (); + waiting_for_input = owfi; } } #endif commit 5a48c99696275d896e24db16f1cd0f38569c7fca Author: Alan Third Date: Sat Jun 12 19:04:02 2021 +0100 Move parent frame setting code into EmacsWindow * src/nsterm.m (ns_make_frame_visible): (ns_set_parent_frame): ([EmacsWindow initWithEmacsFrame:fullscreen:screen:]): Use new method. ([EmacsWindow setParentChildRelationships]): New method. diff --git a/src/nsterm.h b/src/nsterm.h index f7ab8236b4..404c714005 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -418,6 +418,7 @@ typedef id instancetype; - (instancetype)initWithEmacsFrame:(struct frame *)f; - (instancetype)initWithEmacsFrame:(struct frame *)f fullscreen:(BOOL)fullscreen screen:(NSScreen *)screen; +- (void)setParentChildRelationships; - (NSInteger)borderWidth; - (BOOL)restackWindow:(NSWindow *)win above:(BOOL)above; - (void)setAppearance; diff --git a/src/nsterm.m b/src/nsterm.m index ba334d5fe9..29a86e4148 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1453,7 +1453,7 @@ -(void)remove if (!FRAME_VISIBLE_P (f)) { EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f); - NSWindow *window = [view window]; + EmacsWindow *window = (EmacsWindow *)[view window]; SET_FRAME_VISIBLE (f, 1); ns_raise_frame (f, ! FRAME_NO_FOCUS_ON_MAP (f)); @@ -1476,11 +1476,8 @@ -(void)remove relationship, so reinstate it. */ if ([window parentWindow] == nil && FRAME_PARENT_FRAME (f) != NULL) { - NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window]; - block_input (); - [parent addChildWindow: window - ordered: NSWindowAbove]; + [window setParentChildRelationships]; unblock_input (); /* If the parent frame moved while the child frame was @@ -1783,7 +1780,6 @@ Hide the window (X11 semantics) -------------------------------------------------------------------------- */ { struct frame *p = NULL; - NSWindow *parent, *child; NSTRACE ("ns_set_parent_frame"); @@ -1796,72 +1792,11 @@ Hide the window (X11 semantics) error ("Invalid specification of `parent-frame'"); } - if (p != FRAME_PARENT_FRAME (f)) - { - block_input (); - child = [FRAME_NS_VIEW (f) window]; - -#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f); -#endif + fset_parent_frame (f, new_value); - if ([child parentWindow] != nil) - { -#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - parent = [child parentWindow]; -#endif - - [[child parentWindow] removeChildWindow:child]; -#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 - if ([child respondsToSelector:@selector(setAccessibilitySubrole:)]) -#endif - [child setAccessibilitySubrole:NSAccessibilityStandardWindowSubrole]; -#endif -#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - if (NILP (new_value)) - { - NSTRACE ("child setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary"); - [child setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; - // if current parent in fullscreen and no new parent make child fullscreen - while (parent) { - if (([parent styleMask] & NSWindowStyleMaskFullScreen) != 0) - { - [view toggleFullScreen:child]; - break; - } - // check all parents - parent = [parent parentWindow]; - } - } -#endif - } - - if (!NILP (new_value)) - { -#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 - // child frame must not be in fullscreen - if ([view fsIsNative] && [view isFullscreen]) - [view toggleFullScreen:child]; - NSTRACE ("child setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary"); - [child setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; -#endif - parent = [FRAME_NS_VIEW (p) window]; - - [parent addChildWindow: child - ordered: NSWindowAbove]; -#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 - if ([child respondsToSelector:@selector(setAccessibilitySubrole:)]) -#endif - [child setAccessibilitySubrole:NSAccessibilityFloatingWindowSubrole]; -#endif - } - - unblock_input (); - - fset_parent_frame (f, new_value); - } + block_input (); + [(EmacsWindow *)[FRAME_NS_VIEW (f) window] setParentChildRelationships]; + unblock_input (); } void @@ -7574,7 +7509,7 @@ - (void)updateCollectionBehavior NSWindowCollectionBehavior b = [win collectionBehavior]; if (ns_use_native_fullscreen) { - if ([win parentWindow]) + if (FRAME_PARENT_FRAME (emacsframe)) { b &= ~NSWindowCollectionBehaviorFullScreenPrimary; b |= NSWindowCollectionBehaviorFullScreenAuxiliary; @@ -8336,22 +8271,7 @@ - (instancetype) initWithEmacsFrame:(struct frame *)f [self setTitlebarAppearsTransparent:FRAME_NS_TRANSPARENT_TITLEBAR (f)]; #endif - #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 - if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7) -#endif - if (FRAME_PARENT_FRAME (f)) - [self setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; - else - [self setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; -#endif - - if (FRAME_PARENT_FRAME (f) != NULL) - { - NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window]; - [parent addChildWindow:self - ordered:NSWindowAbove]; - } + [self setParentChildRelationships]; if (FRAME_Z_GROUP (f) != z_group_none) [self setLevel:NSNormalWindowLevel + (FRAME_Z_GROUP_BELOW (f) ? -1 : 1)]; @@ -8432,6 +8352,96 @@ - (NSInteger) borderWidth } +- (void)setParentChildRelationships + /* After certain operations, for example making a frame visible or + resetting the NSWindow through modifying the undecorated status, + the parent/child relationship may be broken. We can also use + this method to set them, as long as the frame struct already has + the correct relationship set. */ +{ + NSTRACE ("[EmacsWindow setParentChildRelationships]"); + + Lisp_Object frame, tail; + EmacsView *ourView = (EmacsView *)[self delegate]; + struct frame *ourFrame = ourView->emacsframe; + struct frame *parentFrame = FRAME_PARENT_FRAME (ourFrame); + EmacsWindow *oldParentWindow = (EmacsWindow *)[self parentWindow]; + + +#ifdef NS_IMPL_COCOA + /* We have to set the accesibility subroles and/or the collection + behaviors early otherwise child windows may not go fullscreen as + expected later. */ + +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101000 + if ([child respondsToSelector:@selector(setAccessibilitySubrole:)]) +#endif + /* Set the accessibilty subroles. */ + if (parentFrame) + [self setAccessibilitySubrole:NSAccessibilityFloatingWindowSubrole]; + else + [self setAccessibilitySubrole:NSAccessibilityStandardWindowSubrole]; + +#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 + [ourView updateCollectionBehavior]; +#endif +#endif + + + /* Check if we have an incorrectly set parent. */ + if ((! parentFrame && oldParentWindow) + || (parentFrame && oldParentWindow + && ((EmacsView *)[oldParentWindow delegate])->emacsframe != parentFrame)) + { + [[self parentWindow] removeChildWindow:self]; + +#ifdef NS_IMPL_COCOA +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 + if ([ourView respondsToSelector:@selector (toggleFullScreen)] +#endif + /* If we are the descendent of a fullscreen window and we + have no new parent, go fullscreen. */ + { + NSWindow *parent = (NSWindow *)oldParentWindow; + while (parent) + { + if (([parent styleMask] & NSWindowStyleMaskFullScreen) != 0) + { + [ourView toggleFullScreen:self]; + break; + } + parent = [parent parentWindow]; + } + } +#endif + } + + if (parentFrame) + { + NSWindow *parentWindow = [FRAME_NS_VIEW (parentFrame) window]; + +#ifdef NS_IMPL_COCOA +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 + if ([ourView respondsToSelector:@selector (toggleFullScreen)] +#endif + /* Child frames must not be fullscreen. */ + if ([ourView fsIsNative] && [ourView isFullscreen]) + [ourView toggleFullScreen:self]; +#endif + + [parentWindow addChildWindow:self + ordered:NSWindowAbove]; + } + + /* Check our child windows are configured correctly. */ + FOR_EACH_FRAME (tail, frame) + { + if (FRAME_PARENT_FRAME (XFRAME (frame)) == ourFrame) + [(EmacsWindow *)[FRAME_NS_VIEW (XFRAME (frame)) window] setParentChildRelationships]; + } +} + + /* It seems the only way to reorder child frames is by removing them from the parent and then reattaching them in the correct order. */ commit 0b5cf4850990ff7b32264d2a174843fe72203cd0 Author: Alan Third Date: Sat Jun 12 12:52:15 2021 +0100 Move NS port toolbar handling to the window * src/nsmenu.m (free_frame_tool_bar): (update_frame_tool_bar): Remove wait_for_tool_bar and get the toolbar from the window. * src/nsterm.h (EmacsView): Remove toolbar and wait_for_tool_bar. * src/nsterm.m (ns_update_begin): ([EmacsView windowDidEnterFullScreen]): ([EmacsView windowDidExitFullScreen]): Get the toolbar from the window, not the view. ([EmacsView dealloc]): Remove toolbar from view. ([EmacsView createToolbar:]): Move method to EmacsWindow. ([EmacsView initFrameFromEmacs:]): Don't create toolbar here any more. ([EmacsView toolbar]): Remove method. ([EmacsWindow initWithEmacsFrame:fullscreen:screen:]): Create toolbar here. ([EmacsWindow createToolbar:]): Moved from EmacsView. ([EmacsWindow dealloc]): Make sure we clean up the toolbar after closing the window. diff --git a/src/nsmenu.m b/src/nsmenu.m index 1b03fe91a8..673c0423d0 100644 --- a/src/nsmenu.m +++ b/src/nsmenu.m @@ -991,12 +991,11 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item NSTRACE ("free_frame_tool_bar"); block_input (); - view->wait_for_tool_bar = NO; /* Note: This triggers an animation, which calls windowDidResize repeatedly. */ f->output_data.ns->in_animation = 1; - [[view toolbar] setVisible: NO]; + [[[view window] toolbar] setVisible: NO]; f->output_data.ns->in_animation = 0; unblock_input (); @@ -1009,12 +1008,12 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item -------------------------------------------------------------------------- */ { int i, k = 0; - EmacsView *view = FRAME_NS_VIEW (f); - EmacsToolbar *toolbar = [view toolbar]; + NSWindow *window = [FRAME_NS_VIEW (f) window]; + EmacsToolbar *toolbar = (EmacsToolbar *)[window toolbar]; NSTRACE ("update_frame_tool_bar"); - if (view == nil || toolbar == nil) return; + if (window == nil || toolbar == nil) return; block_input (); #ifdef NS_IMPL_COCOA @@ -1120,13 +1119,6 @@ - (void)menu:(NSMenu *)menu willHighlightItem:(NSMenuItem *)item [newDict release]; } #endif - - if (view->wait_for_tool_bar && FRAME_TOOLBAR_HEIGHT (f) > 0) - { - view->wait_for_tool_bar = NO; - [view setNeedsDisplay: YES]; - } - unblock_input (); } diff --git a/src/nsterm.h b/src/nsterm.h index 40206cc4de..f7ab8236b4 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -453,9 +453,7 @@ typedef id instancetype; @public struct frame *emacsframe; int scrollbarsNeedingUpdate; - EmacsToolbar *toolbar; NSRect ns_userRect; - BOOL wait_for_tool_bar; } /* AppKit-side interface */ @@ -469,9 +467,7 @@ typedef id instancetype; /* Emacs-side interface */ - (instancetype) initFrameFromEmacs: (struct frame *) f; -- (void) createToolbar: (struct frame *)f; - (void) setWindowClosing: (BOOL)closing; -- (EmacsToolbar *) toolbar; - (void) deleteWorkingText; - (void) handleFS; - (void) setFSValue: (int)value; diff --git a/src/nsterm.m b/src/nsterm.m index b8b306e685..ba334d5fe9 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1026,7 +1026,7 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) { // Fix reappearing tool bar in fullscreen for Mac OS X 10.7 BOOL tbar_visible = FRAME_EXTERNAL_TOOL_BAR (f) ? YES : NO; - NSToolbar *toolbar = [FRAME_NS_VIEW (f) toolbar]; + NSToolbar *toolbar = [[FRAME_NS_VIEW (f) window] toolbar]; if (! tbar_visible != ! [toolbar isVisible]) [toolbar setVisible: tbar_visible]; } @@ -1745,9 +1745,6 @@ Hide the window (X11 semantics) newWindow = [[EmacsWindow alloc] initWithEmacsFrame:f]; - if (!FRAME_UNDECORATED (f)) - [view createToolbar: f]; - if ([oldWindow isKeyWindow]) [newWindow makeKeyAndOrderFront:NSApp]; @@ -6074,7 +6071,6 @@ - (void)dealloc name:NSViewFrameDidChangeNotification object:nil]; - [toolbar release]; if (fs_state == FULLSCREEN_BOTH) [nonfs_window release]; [super dealloc]; @@ -7155,34 +7151,6 @@ - (BOOL)isOpaque } -- (void)createToolbar: (struct frame *)f -{ - EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f); - NSWindow *window = [view window]; - - toolbar = [[EmacsToolbar alloc] initForView: self withIdentifier: - [NSString stringWithFormat: @"Emacs Frame %d", - ns_window_num]]; - [toolbar setVisible: NO]; - [window setToolbar: toolbar]; - - /* Don't set frame garbaged until tool bar is up to date? - This avoids an extra clear and redraw (flicker) at frame creation. */ - if (FRAME_EXTERNAL_TOOL_BAR (f)) wait_for_tool_bar = YES; - else wait_for_tool_bar = NO; - - -#ifdef NS_IMPL_COCOA - { - NSButton *toggleButton; - toggleButton = [window standardWindowButton: NSWindowToolbarButton]; - [toggleButton setTarget: self]; - [toggleButton setAction: @selector (toggleToolbar: )]; - } -#endif -} - - - (instancetype) initFrameFromEmacs: (struct frame *)f { NSTRACE ("[EmacsView initFrameFromEmacs:]"); @@ -7234,10 +7202,6 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f if (ns_drag_types) [self registerForDraggedTypes: ns_drag_types]; - /* toolbar support */ - if (! FRAME_UNDECORATED (f)) - [self createToolbar: f]; - #if !defined (NS_IMPL_COCOA) \ || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090 @@ -7517,7 +7481,7 @@ - (void)windowDidEnterFullScreen /* provided for direct calls */ [NSApp setPresentationOptions: options]; } #endif - [toolbar setVisible:tbar_visible]; + [[[self window]toolbar] setVisible:tbar_visible]; } } @@ -7560,12 +7524,12 @@ - (void)windowDidExitFullScreen /* provided for direct calls */ #endif if (FRAME_EXTERNAL_TOOL_BAR (emacsframe)) { - [toolbar setVisible:YES]; + [[[self window] toolbar] setVisible:YES]; update_frame_tool_bar (emacsframe); [[self window] display]; } else - [toolbar setVisible:NO]; + [[[self window] toolbar] setVisible:NO]; if (next_maximized != -1) [[self window] performZoom:self]; @@ -7864,12 +7828,6 @@ - (instancetype)menuDown: sender } -- (EmacsToolbar *)toolbar -{ - return toolbar; -} - - /* This gets called on toolbar button click. */ - (instancetype)toolbarClicked: (id)item { @@ -8421,6 +8379,10 @@ - (instancetype) initWithEmacsFrame:(struct frame *)f if ([col alphaComponent] != (EmacsCGFloat) 1.0) [self setOpaque:NO]; + /* toolbar support */ + if (! FRAME_UNDECORATED (f)) + [self createToolbar:f]; + /* macOS Sierra automatically enables tabbed windows. We can't allow this to be enabled until it's available on a Free system. Currently it only happens by accident and is buggy anyway. */ @@ -8434,6 +8396,36 @@ - (instancetype) initWithEmacsFrame:(struct frame *)f } +- (void)createToolbar: (struct frame *)f +{ + EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f); + + EmacsToolbar *toolbar = [[EmacsToolbar alloc] + initForView:view + withIdentifier:[NSString stringWithLispString:f->name]]; + [self setToolbar:toolbar]; + + update_frame_tool_bar (f); + +#ifdef NS_IMPL_COCOA + { + NSButton *toggleButton; + toggleButton = [self standardWindowButton:NSWindowToolbarButton]; + [toggleButton setTarget:view]; + [toggleButton setAction:@selector (toggleToolbar:)]; + } +#endif +} + +- (void)dealloc +{ + NSTRACE ("[EmacsWindow dealloc]"); + + /* We need to release the toolbar ourselves. */ + [[self toolbar] release]; + [super dealloc]; +} + - (NSInteger) borderWidth { return NSWidth ([self frame]) - NSWidth ([[self contentView] frame]); commit 1ba02d85a964e1b2c6a9735cd3decdc524e06dc1 Author: Alan Third Date: Sat Jun 12 10:25:47 2021 +0100 Fix macOS live resize drawing * src/nsterm.m ([EmacsView layout]): ([EmacsView layoutSublayersOfLayer:]): Rename layout to layoutSublayersOfLayer. diff --git a/src/nsterm.m b/src/nsterm.m index a005c4e813..b8b306e685 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -8040,24 +8040,33 @@ - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect redisplay before drawing. This used to be done in viewWillDraw, but with the custom layer - that method is not called. */ -- (void)layout -{ - [super layout]; - - /* If there is IO going on when redisplay is run here Emacs - crashes. I think it's because this code will always be run - within the run loop and for whatever reason processing input - is dangerous. This technique was stolen wholesale from - nsmenu.m and seems to work. */ - bool owfi = waiting_for_input; - waiting_for_input = 0; - block_input (); + that method is not called. We cannot call redisplay directly from + [NSView layout], because it may trigger another round of layout by + changing the frame size and recursive layout calls are banned. It + appears to be safe to call redisplay here. */ +- (void)layoutSublayersOfLayer:(CALayer *)layer +{ + if (!redisplaying_p && FRAME_GARBAGED_P (emacsframe)) + { + /* If there is IO going on when redisplay is run here Emacs + crashes. I think it's because this code will always be run + within the run loop and for whatever reason processing input + is dangerous. This technique was stolen wholesale from + nsmenu.m and seems to work. - redisplay (); + FIXME: I can't provoke a crash using layoutSublayersOfLayer, + however I can't understand why it would be different from + viewWillDraw. I'll leave this commented out for now, but if + nobody reports a crash it can be removed. */ + // bool owfi = waiting_for_input; + // waiting_for_input = 0; + // block_input (); - unblock_input (); - waiting_for_input = owfi; + redisplay (); + + // unblock_input (); + // waiting_for_input = owfi; + } } #endif commit 960f3fc589d8d021e1ed1a505e660929e4c13d0a Author: Alan Third Date: Wed Jun 9 17:57:00 2021 +0100 Change NS port resize detection * src/nsterm.m ([EmacsView windowDidResize:]): Remove function, it's not performing a useful function any more. ([EmacsView viewDidResize]): ([EmacsView resizeWithOldSuperviewSize:]): Replace viewDidResize with resizeWithOldSuperviewSize. ([EmacsView initFrameFromEmacs:]): Remove the view resize notification as we don't need it any more. diff --git a/src/nsterm.m b/src/nsterm.m index 569365d5e4..a005c4e813 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -7020,43 +7020,6 @@ - (NSSize)windowWillResize: (NSWindow *)sender toSize: (NSSize)frameSize } -- (void)windowDidResize: (NSNotification *)notification -{ - NSTRACE ("[EmacsView windowDidResize:]"); - if (!FRAME_LIVE_P (emacsframe)) - { - NSTRACE_MSG ("Ignored (frame dead)"); - return; - } - if (emacsframe->output_data.ns->in_animation) - { - NSTRACE_MSG ("Ignored (in animation)"); - return; - } - - if (! [self fsIsNative]) - { - NSWindow *theWindow = [notification object]; - /* We can get notification on the non-FS window when in - fullscreen mode. */ - if ([self window] != theWindow) return; - } - - NSTRACE_RECT ("frame", [[notification object] frame]); - -#ifdef NS_IMPL_GNUSTEP - NSWindow *theWindow = [notification object]; - - /* In GNUstep, at least currently, it's possible to get a didResize - without getting a willResize, therefore we need to act as if we got - the willResize now. */ - NSSize sz = [theWindow frame].size; - sz = [self windowWillResize: theWindow toSize: sz]; -#endif /* NS_IMPL_GNUSTEP */ - - ns_send_appdefined (-1); -} - #ifdef NS_IMPL_COCOA - (void)viewDidEndLiveResize { @@ -7074,38 +7037,30 @@ - (void)viewDidEndLiveResize #endif /* NS_IMPL_COCOA */ -- (void)viewDidResize:(NSNotification *)notification +- (void)resizeWithOldSuperviewSize: (NSSize)oldSize { - NSRect frame = [self frame]; - int neww, newh, oldw, oldh; + NSRect frame; + int width, height; - if (! FRAME_LIVE_P (emacsframe)) - return; + NSTRACE ("[EmacsView resizeWithOldSuperviewSize:]"); - NSTRACE ("[EmacsView viewDidResize]"); + [super resizeWithOldSuperviewSize:oldSize]; - neww = (int)NSWidth (frame); - newh = (int)NSHeight (frame); - oldw = FRAME_PIXEL_WIDTH (emacsframe); - oldh = FRAME_PIXEL_HEIGHT (emacsframe); + if (! FRAME_LIVE_P (emacsframe)) + return; - /* Don't want to do anything when the view size hasn't changed. */ - if (emacsframe->new_size_p - ? (newh == emacsframe->new_height - && neww == emacsframe->new_width) - : (oldh == newh && oldw == neww)) - { - NSTRACE_MSG ("No change"); - return; - } + frame = [self frame]; + width = (int)NSWidth (frame); + height = (int)NSHeight (frame); - NSTRACE_SIZE ("New size", NSMakeSize (neww, newh)); - NSTRACE_SIZE ("Original size", NSMakeSize (oldw, oldh)); + NSTRACE_SIZE ("New size", NSMakeSize (width, height)); + NSTRACE_SIZE ("Original size", size); - change_frame_size (emacsframe, neww, newh, false, YES, false); + change_frame_size (emacsframe, width, height, false, YES, false); SET_FRAME_GARBAGED (emacsframe); cancel_mouse_face (emacsframe); + ns_send_appdefined (-1); } @@ -7269,7 +7224,7 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f /* These settings mean AppKit will retain the contents of the frame on resize. Unfortunately it also means the frame will not be automatically marked for display, but we can do that ourselves in - viewDidResize. */ + resizeWithOldSuperviewSize. */ [self setWantsLayer:YES]; [self setLayerContentsRedrawPolicy: NSViewLayerContentsRedrawOnSetNeedsDisplay]; @@ -7293,13 +7248,6 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f [NSApp registerServicesMenuSendTypes: ns_send_types returnTypes: [NSArray array]]; - /* Set up view resize notifications. */ - [self setPostsFrameChangedNotifications:YES]; - [[NSNotificationCenter defaultCenter] - addObserver:self - selector:@selector (viewDidResize:) - name:NSViewFrameDidChangeNotification object:nil]; - ns_window_num++; return self; } commit 1535c81f77153dd61426246be2e8afd33fa6909a Author: Alan Third Date: Sat Jun 5 12:39:46 2021 +0100 Tidy up NS port OS window handling * src/nsterm.h (EmacsWindow): Move above EmacsView definition and add new method definitions. (EmacsView): Remove redundant bwidth variable, and change NSWindow to EmacsWindow. (EmacsFSWindow): Delete definition. * src/nsterm.m (ns_set_undecorated): Rewrite to work in GNUstep using the new OS window creating methods. ([EmacsView initFrameFromEmacs:]): Move all NSWindow related code to new init method in EmacsWindow, and use said method. ([EmacsView toggleFullScreen:]): Use EmacsWindow instead of NSWindow. ([EmacsWindow initWithEmacsFrame:]): ([EmacsWindow initWithEmacsFrame:fullscreen:screen:]): ([EmacsWindow borderWidth]): New methods. (EmacsFSWindow): Remove implementation. diff --git a/src/nsfns.m b/src/nsfns.m index 454a6fdab6..c40367703d 100644 --- a/src/nsfns.m +++ b/src/nsfns.m @@ -947,11 +947,7 @@ Turn the input menu (an NSMenu) into a lisp list for tracking on lisp side. 0, /* x_set_sticky */ 0, /* x_set_tool_bar_position */ 0, /* x_set_inhibit_double_buffering */ -#ifdef NS_IMPL_COCOA ns_set_undecorated, -#else - 0, /* ns_set_undecorated */ -#endif ns_set_parent_frame, 0, /* x_set_skip_taskbar */ ns_set_no_focus_on_map, diff --git a/src/nsterm.h b/src/nsterm.h index c61c698655..40206cc4de 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -406,6 +406,24 @@ typedef id instancetype; @end #endif +/* EmacsWindow */ +@interface EmacsWindow : NSWindow +{ + NSPoint grabOffset; +} + +#ifdef NS_IMPL_GNUSTEP +- (NSInteger) orderedIndex; +#endif + +- (instancetype)initWithEmacsFrame:(struct frame *)f; +- (instancetype)initWithEmacsFrame:(struct frame *)f fullscreen:(BOOL)fullscreen screen:(NSScreen *)screen; +- (NSInteger)borderWidth; +- (BOOL)restackWindow:(NSWindow *)win above:(BOOL)above; +- (void)setAppearance; +@end + + /* ========================================================================== The main Emacs view @@ -429,9 +447,8 @@ typedef id instancetype; NSString *workingText; BOOL processingCompose; int fs_state, fs_before_fs, next_maximized; - int bwidth; int maximized_width, maximized_height; - NSWindow *nonfs_window; + EmacsWindow *nonfs_window; BOOL fs_is_native; @public struct frame *emacsframe; @@ -485,27 +502,6 @@ typedef id instancetype; @end -/* Small utility used for processing resize events under Cocoa. */ -@interface EmacsWindow : NSWindow -{ - NSPoint grabOffset; -} - -#ifdef NS_IMPL_GNUSTEP -- (NSInteger) orderedIndex; -#endif - -- (BOOL)restackWindow:(NSWindow *)win above:(BOOL)above; -- (void)setAppearance; -@end - - -/* Fullscreen version of the above. */ -@interface EmacsFSWindow : EmacsWindow -{ -} -@end - /* ========================================================================== The main menu implementation diff --git a/src/nsterm.m b/src/nsterm.m index b2ee459c42..569365d5e4 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1720,7 +1720,6 @@ Hide the window (X11 semantics) unblock_input (); } -#ifdef NS_IMPL_COCOA void ns_set_undecorated (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) /* -------------------------------------------------------------------------- @@ -1730,45 +1729,37 @@ Hide the window (X11 semantics) dragged, resized, iconified, maximized or deleted with the mouse. If nil, draw the frame with all the elements listed above unless these have been suspended via window manager settings. - - GNUStep cannot change an existing window's style. -------------------------------------------------------------------------- */ { - EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f); - NSWindow *window = [view window]; - NSTRACE ("ns_set_undecorated"); if (!EQ (new_value, old_value)) { + EmacsView *view = (EmacsView *)FRAME_NS_VIEW (f); + NSWindow *oldWindow = [view window]; + NSWindow *newWindow; + block_input (); - if (NILP (new_value)) - { - FRAME_UNDECORATED (f) = false; - [window setStyleMask: ((window.styleMask | FRAME_DECORATED_FLAGS) - ^ FRAME_UNDECORATED_FLAGS)]; + FRAME_UNDECORATED (f) = !NILP (new_value); - [view createToolbar: f]; - } - else - { - [window setToolbar: nil]; - /* Do I need to release the toolbar here? */ + newWindow = [[EmacsWindow alloc] initWithEmacsFrame:f]; - FRAME_UNDECORATED (f) = true; - [window setStyleMask: ((window.styleMask | FRAME_UNDECORATED_FLAGS) - ^ FRAME_DECORATED_FLAGS)]; - } + if (!FRAME_UNDECORATED (f)) + [view createToolbar: f]; + + if ([oldWindow isKeyWindow]) + [newWindow makeKeyAndOrderFront:NSApp]; - /* At this point it seems we don't have an active NSResponder, - so some key presses (TAB) are swallowed by the system. */ - [window makeFirstResponder: view]; + [newWindow setIsVisible:[oldWindow isVisible]]; + if ([oldWindow isMiniaturized]) + [newWindow miniaturize:NSApp]; + + [oldWindow close]; unblock_input (); } } -#endif /* NS_IMPL_COCOA */ void ns_set_parent_frame (struct frame *f, Lisp_Object new_value, Lisp_Object old_value) @@ -7239,12 +7230,6 @@ This avoids an extra clear and redraw (flicker) at frame creation. */ - (instancetype) initFrameFromEmacs: (struct frame *)f { - NSRect r, wr; - Lisp_Object tem; - EmacsWindow *win; - NSColor *col; - NSString *name; - NSTRACE ("[EmacsView initFrameFromEmacs:]"); NSTRACE_MSG ("cols:%d lines:%d", f->text_cols, f->text_lines); @@ -7266,9 +7251,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f nonfs_window = nil; ns_userRect = NSMakeRect (0, 0, 0, 0); - r = NSMakeRect (0, 0, FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols), - FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines)); - [self initWithFrame: r]; + [self initWithFrame: + NSMakeRect (0, 0, FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols), + FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines))]; [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable]; FRAME_NS_VIEW (f) = self; @@ -7278,37 +7263,7 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f maximizing_resize = NO; #endif - win = [[EmacsWindow alloc] - initWithContentRect: r - styleMask: (FRAME_UNDECORATED (f) - ? FRAME_UNDECORATED_FLAGS - : FRAME_DECORATED_FLAGS) - backing: NSBackingStoreBuffered - defer: YES]; - -#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 -#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 - if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7) -#endif - if (FRAME_PARENT_FRAME (f)) - [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; - else - [win setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; -#endif - - wr = [win frame]; - bwidth = f->border_width = wr.size.width - r.size.width; - - [win setAcceptsMouseMovedEvents: YES]; - [win setDelegate: self]; -#if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090 -#if MAC_OS_X_VERSION_MAX_ALLOWED > 1090 - if ([win respondsToSelector: @selector(useOptimizedDrawing:)]) -#endif - [win useOptimizedDrawing: YES]; -#endif - - [[win contentView] addSubview: self]; + [[EmacsWindow alloc] initWithEmacsFrame:f]; #ifdef NS_IMPL_COCOA /* These settings mean AppKit will retain the contents of the frame @@ -7324,65 +7279,10 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f if (ns_drag_types) [self registerForDraggedTypes: ns_drag_types]; - tem = f->name; - name = NILP (tem) ? @"Emacs" : [NSString stringWithLispString:tem]; - [win setTitle: name]; - /* toolbar support */ if (! FRAME_UNDECORATED (f)) [self createToolbar: f]; - - [win setAppearance]; - -#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 - if ([win respondsToSelector: @selector(titlebarAppearsTransparent)]) - win.titlebarAppearsTransparent = FRAME_NS_TRANSPARENT_TITLEBAR (f); -#endif - - tem = f->icon_name; - if (!NILP (tem)) - [win setMiniwindowTitle: - [NSString stringWithLispString:tem]]; - - if (FRAME_PARENT_FRAME (f) != NULL) - { - NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window]; - [parent addChildWindow: win - ordered: NSWindowAbove]; - } - - if (FRAME_Z_GROUP (f) != z_group_none) - win.level = NSNormalWindowLevel - + (FRAME_Z_GROUP_BELOW (f) ? -1 : 1); - - { - NSScreen *screen = [win screen]; - - if (screen != 0) - { - NSPoint pt = NSMakePoint - (IN_BOUND (-SCREENMAX, f->left_pos - + NS_PARENT_WINDOW_LEFT_POS (f), SCREENMAX), - IN_BOUND (-SCREENMAX, - NS_PARENT_WINDOW_TOP_POS (f) - f->top_pos, - SCREENMAX)); - - [win setFrameTopLeftPoint: pt]; - - NSTRACE_RECT ("new frame", [win frame]); - } - } - - [win makeFirstResponder: self]; - - col = ns_lookup_indexed_color (NS_FACE_BACKGROUND - (FACE_FROM_ID (emacsframe, DEFAULT_FACE_ID)), - emacsframe); - [win setBackgroundColor: col]; - if ([col alphaComponent] != (EmacsCGFloat) 1.0) - [win setOpaque: NO]; - #if !defined (NS_IMPL_COCOA) \ || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090 #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090 @@ -7400,14 +7300,6 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f selector:@selector (viewDidResize:) name:NSViewFrameDidChangeNotification object:nil]; - /* macOS Sierra automatically enables tabbed windows. We can't - allow this to be enabled until it's available on a Free system. - Currently it only happens by accident and is buggy anyway. */ -#ifdef NS_IMPL_COCOA - if ([win respondsToSelector: @selector(setTabbingMode:)]) - [win setTabbingMode: NSWindowTabbingModeDisallowed]; -#endif - ns_window_num++; return self; } @@ -7797,7 +7689,7 @@ - (void)updateCollectionBehavior - (void)toggleFullScreen: (id)sender { - NSWindow *w, *fw; + EmacsWindow *w, *fw; BOOL onFirstScreen; struct frame *f; NSRect r, wr; @@ -7816,7 +7708,7 @@ - (void)toggleFullScreen: (id)sender return; } - w = [self window]; + w = (EmacsWindow *)[self window]; onFirstScreen = [[w screen] isEqual:[[NSScreen screens] objectAtIndex:0]]; f = emacsframe; wr = [w frame]; @@ -7851,27 +7743,9 @@ - (void)toggleFullScreen: (id)sender #endif } - fw = [[EmacsFSWindow alloc] - initWithContentRect:[w contentRectForFrameRect:wr] - styleMask:NSWindowStyleMaskBorderless - backing:NSBackingStoreBuffered - defer:YES - screen:screen]; - - [fw setContentView:[w contentView]]; - [fw setTitle:[w title]]; - [fw setDelegate:self]; - [fw setAcceptsMouseMovedEvents: YES]; -#if !defined (NS_IMPL_COCOA) \ - || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090 -#if MAC_OS_X_VERSION_MAX_ALLOWED > 1090 - if ([fw respondsToSelector: @selector(useOptimizedDrawing:)]) -#endif - [fw useOptimizedDrawing: YES]; -#endif - [fw setBackgroundColor: col]; - if ([col alphaComponent] != (EmacsCGFloat) 1.0) - [fw setOpaque: NO]; + fw = [[EmacsWindow alloc] initWithEmacsFrame:emacsframe + fullscreen:YES + screen:screen]; f->border_width = 0; @@ -7879,7 +7753,6 @@ - (void)toggleFullScreen: (id)sender [self windowWillEnterFullScreen]; [fw makeKeyAndOrderFront:NSApp]; - [fw makeFirstResponder:self]; [w orderOut:self]; r = [fw frameRectForContentRect:[screen frame]]; [fw setFrame: r display:YES animate:ns_use_fullscreen_animation]; @@ -7906,7 +7779,7 @@ - (void)toggleFullScreen: (id)sender if ([col alphaComponent] != (EmacsCGFloat) 1.0) [w setOpaque: NO]; - f->border_width = bwidth; + f->border_width = [w borderWidth]; // To do: consider using [NSNotificationCenter postNotificationName:] to // send notifications. @@ -8483,6 +8356,133 @@ - (int) fullscreenState @implementation EmacsWindow + +- (instancetype) initWithEmacsFrame:(struct frame *)f +{ + return [self initWithEmacsFrame:f fullscreen:NO screen:nil]; +} + + +- (instancetype) initWithEmacsFrame:(struct frame *)f + fullscreen:(BOOL)fullscreen + screen:(NSScreen *)screen +{ + NSWindowStyleMask styleMask; + + NSTRACE ("[EmacsWindow initWithEmacsFrame:fullscreen:screen:]"); + + if (fullscreen) + styleMask = NSWindowStyleMaskBorderless; + else if (FRAME_UNDECORATED (f)) + styleMask = FRAME_UNDECORATED_FLAGS; + else + styleMask = FRAME_DECORATED_FLAGS; + + + self = [super initWithContentRect: + NSMakeRect (0, 0, + FRAME_TEXT_COLS_TO_PIXEL_WIDTH (f, f->text_cols), + FRAME_TEXT_LINES_TO_PIXEL_HEIGHT (f, f->text_lines)) + styleMask:styleMask + backing:NSBackingStoreBuffered + defer:YES + screen:screen]; + if (self) + { + NSString *name; + NSColor *col; + NSScreen *screen = [self screen]; + EmacsView *view = FRAME_NS_VIEW (f); + + [self setDelegate:view]; + [[self contentView] addSubview:view]; + [self makeFirstResponder:view]; + +#if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090 +#if MAC_OS_X_VERSION_MAX_ALLOWED > 1090 + if ([self respondsToSelector: @selector(useOptimizedDrawing:)]) +#endif + [self useOptimizedDrawing:YES]; +#endif + + [self setAcceptsMouseMovedEvents:YES]; + + name = NILP (f->name) ? @"Emacs" : [NSString stringWithLispString:f->name]; + [self setTitle:name]; + + if (!NILP (f->icon_name)) + [self setMiniwindowTitle: + [NSString stringWithLispString:f->icon_name]]; + + [self setAppearance]; + +#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 + if ([self respondsToSelector:@selector(titlebarAppearsTransparent)]) + [self setTitlebarAppearsTransparent:FRAME_NS_TRANSPARENT_TITLEBAR (f)]; +#endif + + #if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 +#if MAC_OS_X_VERSION_MIN_REQUIRED < 1070 + if (NSAppKitVersionNumber >= NSAppKitVersionNumber10_7) +#endif + if (FRAME_PARENT_FRAME (f)) + [self setCollectionBehavior:NSWindowCollectionBehaviorFullScreenAuxiliary]; + else + [self setCollectionBehavior:NSWindowCollectionBehaviorFullScreenPrimary]; +#endif + + if (FRAME_PARENT_FRAME (f) != NULL) + { + NSWindow *parent = [FRAME_NS_VIEW (FRAME_PARENT_FRAME (f)) window]; + [parent addChildWindow:self + ordered:NSWindowAbove]; + } + + if (FRAME_Z_GROUP (f) != z_group_none) + [self setLevel:NSNormalWindowLevel + (FRAME_Z_GROUP_BELOW (f) ? -1 : 1)]; + + if (screen != 0) + { + NSPoint pt = NSMakePoint + (IN_BOUND (-SCREENMAX, f->left_pos + + NS_PARENT_WINDOW_LEFT_POS (f), SCREENMAX), + IN_BOUND (-SCREENMAX, + NS_PARENT_WINDOW_TOP_POS (f) - f->top_pos, + SCREENMAX)); + + [self setFrameTopLeftPoint:pt]; + + NSTRACE_RECT ("new frame", [self frame]); + } + + f->border_width = [self borderWidth]; + + col = ns_lookup_indexed_color (NS_FACE_BACKGROUND + (FACE_FROM_ID (f, DEFAULT_FACE_ID)), + f); + [self setBackgroundColor:col]; + if ([col alphaComponent] != (EmacsCGFloat) 1.0) + [self setOpaque:NO]; + + /* macOS Sierra automatically enables tabbed windows. We can't + allow this to be enabled until it's available on a Free system. + Currently it only happens by accident and is buggy anyway. */ +#ifdef NS_IMPL_COCOA + if ([self respondsToSelector:@selector(setTabbingMode:)]) + [self setTabbingMode:NSWindowTabbingModeDisallowed]; +#endif + } + + return self; +} + + +- (NSInteger) borderWidth +{ + return NSWidth ([self frame]) - NSWidth ([[self contentView] frame]); +} + + /* It seems the only way to reorder child frames is by removing them from the parent and then reattaching them in the correct order. */ @@ -8892,22 +8892,15 @@ - (BOOL)canBecomeKeyWindow { return !FRAME_NO_ACCEPT_FOCUS (((EmacsView *)[self delegate])->emacsframe); } -@end /* EmacsWindow */ - - -@implementation EmacsFSWindow - -- (BOOL)canBecomeKeyWindow -{ - return YES; -} - (BOOL)canBecomeMainWindow + /* Required for fullscreen and undecorated windows. */ { return YES; } -@end +@end /* EmacsWindow */ + /* ========================================================================== commit a4d2c88cdee90a3e4863a62c4ff69896d0c1a347 Author: Alan Third Date: Sat May 29 09:48:51 2021 +0100 Simplify macOS drawing code Convert EmacsSurface into a CALayer subclass so we can use the built-in relationships. Also simplify the macOS versioning code. This will result in more warnings on older versions of macOS but makes reading the code easier. * configure.ac: Add QuartzCore framework. * src/nsterm.h (NS_DRAW_TO_BUFFER): Remove define and all references. (EmacsSurface, EmacsLayer): Rename EmacsSurface to EmacsLayer and modify the definition to fit the new function. * src/nsterm.m (ns_update_begin): (ns_update_end): (ns_focus): (ns_unfocus): Use the new overridden lockFocus and unlockFocus and simplify the frame management. ([EmacsView dealloc]): ([EmacsView viewDidResize:]):Don't explicitly release surfaces. ([EmacsView initFrameFromEmacs:]): Move the layer code to after the NSWindow has been created as creating the layer now relies on some of it's properties. ([EmacsView makeBackingLayer]): New function. ([EmacsView lockFocus]): ([EmacsView focusOnDrawingBuffer]): Rename to lockFocus. ([EmacsView unlockFocus]): ([EmacsView unfocusDrawingBuffer]): Rename to unlockFocus. ([EmacsView windowDidChangeBackingProperties]): Don't explicitly release surfaces but reset EmacsLayer properties. ([EmacsView layout]): ([EmacsView viewWillDraw]): Rename to layout. ([EmacsView wantsUpdateLayer]): Remove function and change all callers to [EmacsView wantsLayer]. (EmacsSurface, EmacsLayer): Rename to EmacsLayer. ([EmacsSurface getSize]): ([EmacsSurface initWithSize:ColorSpace:Scale:]): Remove methods. ([EmacsSurface initWithColorSpace:]): ([EmacsLayer checkDimensions]): ([EmacsLayer releaseSurfaces]): ([EmacsLayer display]): New functions. * src/nsterm.m ([EmacsLayer dealloc]): Use releaseSurfaces. ([EmacsSurface getContext]): Automatically detect frame property changes and clear the cache if required. Use built-in CALayer properties where available. ([EmacsLayer copyContentsTo:]): Use [CALayer contents] as source. diff --git a/configure.ac b/configure.ac index c924634d5b..79cc56f9a7 100644 --- a/configure.ac +++ b/configure.ac @@ -5660,7 +5660,8 @@ case "$opsys" in if test "$HAVE_NS" = "yes"; then libs_nsgui="-framework AppKit" if test "$NS_IMPL_COCOA" = "yes"; then - libs_nsgui="$libs_nsgui -framework IOKit -framework Carbon -framework IOSurface" + libs_nsgui="$libs_nsgui -framework IOKit -framework Carbon \ + -framework IOSurface -framework QuartzCore" fi else libs_nsgui= diff --git a/src/nsterm.h b/src/nsterm.h index 57c1e4cbae..c61c698655 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -348,16 +348,6 @@ typedef id instancetype; #endif -/* macOS 10.14 and above cannot draw directly "to the glass" and - therefore we draw to an offscreen buffer and swap it in when the - toolkit wants to draw the frame. GNUstep and macOS 10.7 and below - do not support this method, so we revert to drawing directly to the - glass. */ -#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101400 -#define NS_DRAW_TO_BUFFER 1 -#endif - - /* ========================================================================== NSColor, EmacsColor category. @@ -423,7 +413,7 @@ typedef id instancetype; ========================================================================== */ @class EmacsToolbar; -@class EmacsSurface; +@class EmacsLayer; #ifdef NS_IMPL_COCOA @interface EmacsView : NSView @@ -443,9 +433,6 @@ typedef id instancetype; int maximized_width, maximized_height; NSWindow *nonfs_window; BOOL fs_is_native; -#ifdef NS_DRAW_TO_BUFFER - EmacsSurface *surface; -#endif @public struct frame *emacsframe; int scrollbarsNeedingUpdate; @@ -483,9 +470,9 @@ typedef id instancetype; #endif - (int)fullscreenState; -#ifdef NS_DRAW_TO_BUFFER -- (void)focusOnDrawingBuffer; -- (void)unfocusDrawingBuffer; +#ifdef NS_IMPL_COCOA +- (void)lockFocus; +- (void)unlockFocus; #endif - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect; @@ -714,23 +701,17 @@ typedef id instancetype; + (CGFloat)scrollerWidth; @end -#ifdef NS_DRAW_TO_BUFFER -@interface EmacsSurface : NSObject +#ifdef NS_IMPL_COCOA +@interface EmacsLayer : CALayer { NSMutableArray *cache; - NSSize size; CGColorSpaceRef colorSpace; IOSurfaceRef currentSurface; - IOSurfaceRef lastSurface; CGContextRef context; - CGFloat scale; } -- (id) initWithSize: (NSSize)s ColorSpace: (CGColorSpaceRef)cs Scale: (CGFloat)scale; -- (void) dealloc; -- (NSSize) getSize; +- (id) initWithColorSpace: (CGColorSpaceRef)cs; +- (void) setColorSpace: (CGColorSpaceRef)cs; - (CGContextRef) getContext; -- (void) releaseContext; -- (IOSurfaceRef) getSurface; @end #endif diff --git a/src/nsterm.m b/src/nsterm.m index 853c0fa2fa..b2ee459c42 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -70,9 +70,6 @@ Updated by Christian Limpach (chris@nice.ch) #ifdef NS_IMPL_COCOA #include "macfont.h" #include -#endif - -#ifdef NS_DRAW_TO_BUFFER #include #endif @@ -272,9 +269,6 @@ - (NSColor *)colorUsingDefaultColorSpace /* display update */ static struct frame *ns_updating_frame; -#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400 -static NSView *focus_view = NULL; -#endif static int ns_window_num = 0; static BOOL gsaved = NO; #ifdef NS_IMPL_COCOA @@ -1039,26 +1033,7 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) #endif ns_updating_frame = f; -#ifdef NS_DRAW_TO_BUFFER -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - if ([FRAME_NS_VIEW (f) wantsUpdateLayer]) - { -#endif - [view focusOnDrawingBuffer]; -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - } - else - { -#endif -#endif /* NS_DRAW_TO_BUFFER */ - -#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - [view lockFocus]; -#endif -#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - } -#endif - + [view lockFocus]; } @@ -1069,39 +1044,21 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) external (RIF) call; for whole frame, called after gui_update_window_end -------------------------------------------------------------------------- */ { -#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400 EmacsView *view = FRAME_NS_VIEW (f); -#endif NSTRACE_WHEN (NSTRACE_GROUP_UPDATES, "ns_update_end"); /* if (f == MOUSE_HL_INFO (f)->mouse_face_mouse_frame) */ MOUSE_HL_INFO (f)->mouse_face_defer = 0; -#ifdef NS_DRAW_TO_BUFFER -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - if ([FRAME_NS_VIEW (f) wantsUpdateLayer]) - { -#endif - [FRAME_NS_VIEW (f) unfocusDrawingBuffer]; -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - } - else - { -#endif -#endif /* NS_DRAW_TO_BUFFER */ - -#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - block_input (); - - [view unlockFocus]; - [[view window] flushWindow]; + block_input (); - unblock_input (); -#endif -#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - } + [view unlockFocus]; +#if defined (NS_IMPL_GNUSTEP) + [[view window] flushWindow]; #endif + + unblock_input (); ns_updating_frame = NULL; } @@ -1116,8 +1073,6 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) the entire window. -------------------------------------------------------------------------- */ { - EmacsView *view = FRAME_NS_VIEW (f); - NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "ns_focus"); if (r != NULL) { @@ -1126,39 +1081,10 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) if (f != ns_updating_frame) { -#ifdef NS_DRAW_TO_BUFFER -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - if ([FRAME_NS_VIEW (f) wantsUpdateLayer]) - { -#endif - [view focusOnDrawingBuffer]; -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - } - else - { -#endif -#endif /* NS_DRAW_TO_BUFFER */ - -#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - if (view != focus_view) - { - if (focus_view != NULL) - { - [focus_view unlockFocus]; - [[focus_view window] flushWindow]; - } - - if (view) - [view lockFocus]; - focus_view = view; - } -#endif -#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - } -#endif + EmacsView *view = FRAME_NS_VIEW (f); + [view lockFocus]; } - /* clipping */ if (r) { @@ -1186,35 +1112,14 @@ static NSRect constrain_frame_rect(NSRect frameRect, bool isFullscreen) gsaved = NO; } -#ifdef NS_DRAW_TO_BUFFER - #if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - if ([FRAME_NS_VIEW (f) wantsUpdateLayer]) - { -#endif - if (! ns_updating_frame) - [FRAME_NS_VIEW (f) unfocusDrawingBuffer]; - [FRAME_NS_VIEW (f) setNeedsDisplay:YES]; -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - } - else + if (f != ns_updating_frame) { + EmacsView *view = FRAME_NS_VIEW (f); + [view unlockFocus]; +#if defined (NS_IMPL_GNUSTEP) + [[view window] flushWindow]; #endif -#endif /* NS_DRAW_TO_BUFFER */ - -#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - if (f != ns_updating_frame) - { - if (focus_view != NULL) - { - [focus_view unlockFocus]; - [[focus_view window] flushWindow]; - focus_view = NULL; - } - } -#endif -#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400 } -#endif } @@ -1381,7 +1286,7 @@ -(void)remove } } -#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400 +#if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400 static void hide_bell (void) /* -------------------------------------------------------------------------- @@ -6178,10 +6083,6 @@ - (void)dealloc name:NSViewFrameDidChangeNotification object:nil]; -#ifdef NS_DRAW_TO_BUFFER - [surface release]; -#endif - [toolbar release]; if (fs_state == FULLSCREEN_BOTH) [nonfs_window release]; @@ -7192,24 +7093,6 @@ - (void)viewDidResize:(NSNotification *)notification NSTRACE ("[EmacsView viewDidResize]"); -#ifdef NS_DRAW_TO_BUFFER - /* If the buffer size doesn't match the view's backing size, destroy - the buffer and let it be recreated at the correct size later. */ - if ([self wantsUpdateLayer] && surface) - { - NSRect surfaceRect = {{0, 0}, [surface getSize]}; - NSRect frameRect = [[self window] convertRectToBacking:frame]; - - if (!NSEqualRects (frameRect, surfaceRect)) - { - [surface release]; - surface = nil; - - [self setNeedsDisplay:YES]; - } - } -#endif - neww = (int)NSWidth (frame); newh = (int)NSHeight (frame); oldw = FRAME_PIXEL_WIDTH (emacsframe); @@ -7388,16 +7271,6 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f [self initWithFrame: r]; [self setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable]; -#ifdef NS_DRAW_TO_BUFFER - /* These settings mean AppKit will retain the contents of the frame - on resize. Unfortunately it also means the frame will not be - automatically marked for display, but we can do that ourselves in - viewDidResize. */ - [self setLayerContentsRedrawPolicy: - NSViewLayerContentsRedrawOnSetNeedsDisplay]; - [self setLayerContentsPlacement:NSViewLayerContentsPlacementTopLeft]; -#endif - FRAME_NS_VIEW (f) = self; emacsframe = f; #ifdef NS_IMPL_COCOA @@ -7437,6 +7310,17 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f [[win contentView] addSubview: self]; +#ifdef NS_IMPL_COCOA + /* These settings mean AppKit will retain the contents of the frame + on resize. Unfortunately it also means the frame will not be + automatically marked for display, but we can do that ourselves in + viewDidResize. */ + [self setWantsLayer:YES]; + [self setLayerContentsRedrawPolicy: + NSViewLayerContentsRedrawOnSetNeedsDisplay]; + [self setLayerContentsPlacement:NSViewLayerContentsPlacementTopLeft]; +#endif + if (ns_drag_types) [self registerForDraggedTypes: ns_drag_types]; @@ -8201,44 +8085,54 @@ - (instancetype)toggleToolbar: (id)sender } -#ifdef NS_DRAW_TO_BUFFER -- (void)focusOnDrawingBuffer +#ifdef NS_IMPL_COCOA +- (CALayer *)makeBackingLayer; { - CGFloat scale = [[self window] backingScaleFactor]; - - NSTRACE ("[EmacsView focusOnDrawingBuffer]"); + EmacsLayer *l = [[EmacsLayer alloc] + initWithColorSpace:[[[self window] colorSpace] CGColorSpace]]; + [l setDelegate:(id)self]; + [l setContentsScale:[[self window] backingScaleFactor]]; - if (! surface) - { - NSRect frame = [self frame]; - NSSize s = NSMakeSize (NSWidth (frame) * scale, NSHeight (frame) * scale); + return l; +} - surface = [[EmacsSurface alloc] initWithSize:s - ColorSpace:[[[self window] colorSpace] - CGColorSpace] - Scale:scale]; - /* Since we're using NSViewLayerContentsRedrawOnSetNeedsDisplay - the layer's scale factor is not set automatically, so do it - now. */ - [[self layer] setContentsScale:scale]; - } +- (void)lockFocus +{ + NSTRACE ("[EmacsView lockFocus]"); - CGContextRef context = [surface getContext]; + if ([self wantsLayer]) + { + CGContextRef context = [(EmacsLayer*)[self layer] getContext]; - [NSGraphicsContext - setCurrentContext:[NSGraphicsContext - graphicsContextWithCGContext:context - flipped:YES]]; + [NSGraphicsContext + setCurrentContext:[NSGraphicsContext + graphicsContextWithCGContext:context + flipped:YES]]; + } +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 + else + [super lockFocus]; +#endif } -- (void)unfocusDrawingBuffer +- (void)unlockFocus { - NSTRACE ("[EmacsView unfocusDrawingBuffer]"); + NSTRACE ("[EmacsView unlockFocus]"); - [NSGraphicsContext setCurrentContext:nil]; - [self setNeedsDisplay:YES]; + if ([self wantsLayer]) + { + [NSGraphicsContext setCurrentContext:nil]; + [self setNeedsDisplay:YES]; + } +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 + else + { + [super unlockFocus]; + [super flushWindow]; + } +#endif } @@ -8247,18 +8141,19 @@ - (void)windowDidChangeBackingProperties:(NSNotification *)notification { NSTRACE ("EmacsView windowDidChangeBackingProperties:]"); - if ([self wantsUpdateLayer]) + if ([self wantsLayer]) { NSRect frame = [self frame]; + EmacsLayer *layer = (EmacsLayer *)[self layer]; - [surface release]; - surface = nil; + [layer setContentsScale:[[notification object] backingScaleFactor]]; + [layer setColorSpace:[[[notification object] colorSpace] CGColorSpace]]; ns_clear_frame (emacsframe); expose_frame (emacsframe, 0, 0, NSWidth (frame), NSHeight (frame)); } } -#endif /* NS_DRAW_TO_BUFFER */ +#endif /* NS_IMPL_COCOA */ - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect @@ -8267,11 +8162,9 @@ - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect NSTRACE_RECT ("Source", srcRect); NSTRACE_RECT ("Destination", dstRect); -#ifdef NS_DRAW_TO_BUFFER -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - if ([self wantsUpdateLayer]) +#ifdef NS_IMPL_COCOA + if ([self wantsLayer]) { -#endif double scale = [[self window] backingScaleFactor]; CGContextRef context = [[NSGraphicsContext currentContext] CGContext]; int bpp = CGBitmapContextGetBitsPerPixel (context) / 8; @@ -8297,14 +8190,14 @@ - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect (char *) srcPixels + y * rowSize, srcRowSize); -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 } +#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 else { #endif -#endif /* NS_DRAW_TO_BUFFER */ +#endif /* NS_IMPL_COCOA */ -#if !defined (NS_DRAW_TO_BUFFER) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400 +#if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED < 101400 hide_bell(); // Ensure the bell image isn't scrolled. ns_focus (emacsframe, &dstRect, 1); @@ -8313,77 +8206,40 @@ - (void)copyRect:(NSRect)srcRect to:(NSRect)dstRect dstRect.origin.y - srcRect.origin.y)]; ns_unfocus (emacsframe); #endif -#if defined (NS_DRAW_TO_BUFFER) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400 +#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MIN_REQUIRED < 101400 } #endif } -#ifdef NS_DRAW_TO_BUFFER +#ifdef NS_IMPL_COCOA /* If the frame has been garbaged but the toolkit wants to draw, for example when resizing the frame, we end up with a blank screen. Sometimes this results in an unpleasant flicker, so try to - redisplay before drawing. */ -- (void)viewWillDraw -{ - if (FRAME_GARBAGED_P (emacsframe) - && !redisplaying_p - && [self wantsUpdateLayer]) - { - /* If there is IO going on when redisplay is run here Emacs - crashes. I think it's because this code will always be run - within the run loop and for whatever reason processing input - is dangerous. This technique was stolen wholesale from - nsmenu.m and seems to work. */ - bool owfi = waiting_for_input; - waiting_for_input = 0; - block_input (); - - redisplay (); - - unblock_input (); - waiting_for_input = owfi; - } -} - + redisplay before drawing. -- (BOOL)wantsUpdateLayer + This used to be done in viewWillDraw, but with the custom layer + that method is not called. */ +- (void)layout { -#if MAC_OS_X_VERSION_MIN_REQUIRED < 101400 - if (NSAppKitVersionNumber < 1671) - return NO; -#endif - - /* Running on macOS 10.14 or above. */ - return YES; -} + [super layout]; + /* If there is IO going on when redisplay is run here Emacs + crashes. I think it's because this code will always be run + within the run loop and for whatever reason processing input + is dangerous. This technique was stolen wholesale from + nsmenu.m and seems to work. */ + bool owfi = waiting_for_input; + waiting_for_input = 0; + block_input (); -- (void)updateLayer -{ - NSTRACE ("[EmacsView updateLayer]"); - - /* We run redisplay on frames that are garbaged, but marked for - display, before updateLayer is called so if the frame is still - garbaged that means the last redisplay must have refused to - update the frame. */ - if (FRAME_GARBAGED_P (emacsframe)) - return; + redisplay (); - /* This can fail to update the screen if the same surface is - provided twice in a row, even if its contents have changed. - There's a private method, -[CALayer setContentsChanged], that we - could use to force it, but we shouldn't often get the same - surface twice in a row. */ - [surface releaseContext]; - [[self layer] setContents:(id)[surface getSurface]]; - [surface performSelectorOnMainThread:@selector (getContext) - withObject:nil - waitUntilDone:NO]; + unblock_input (); + waiting_for_input = owfi; } #endif - - (void)drawRect: (NSRect)rect { NSTRACE ("[EmacsView drawRect:" NSTRACE_FMT_RECT "]", @@ -9550,7 +9406,7 @@ - (void) scrollWheel: (NSEvent *)theEvent @end /* EmacsScroller */ -#ifdef NS_DRAW_TO_BUFFER +#ifdef NS_IMPL_COCOA /* ========================================================================== @@ -9558,7 +9414,7 @@ - (void) scrollWheel: (NSEvent *)theEvent ========================================================================== */ -@implementation EmacsSurface +@implementation EmacsLayer /* An IOSurface is a pixel buffer that is efficiently copied to VRAM @@ -9571,80 +9427,106 @@ @implementation EmacsSurface ability to draw to the screen at any time, we need to keep a cache of multiple surfaces that we can use at will. - The EmacsSurface class maintains this cache of surfaces, and + The EmacsLayer class maintains this cache of surfaces, and handles the conversion to a CGGraphicsContext that AppKit can use to draw on. The cache is simple: if a free surface is found it is removed from - the cache and set as the "current" surface. Once Emacs is done - with drawing to the current surface, the previous surface that was - drawn to is added to the cache for reuse, and the current one is - set as the last surface. If no free surfaces are found in the - cache then a new one is created. - - When AppKit wants to update the screen, we provide it with the last - surface, as that has the most recent data. - - FIXME: It is possible for the cache to grow if Emacs draws faster - than the surfaces can be drawn to the screen, so there should - probably be some sort of pruning job that removes excess - surfaces. */ + the cache and set as the "current" surface. Emacs draws to the + surface and when the layer wants to update the screen we set it's + contents to the surface and then add it back on to the end of the + cache. If no free surfaces are found in the cache then a new one + is created. */ #define CACHE_MAX_SIZE 2 -- (id) initWithSize: (NSSize)s - ColorSpace: (CGColorSpaceRef)cs - Scale: (CGFloat)scl +- (id) initWithColorSpace: (CGColorSpaceRef)cs { - NSTRACE ("[EmacsSurface initWithSize:ColorSpace:]"); - - [super init]; + NSTRACE ("[EmacsLayer initWithColorSpace:]"); - cache = [[NSMutableArray arrayWithCapacity:CACHE_MAX_SIZE] retain]; - size = s; - colorSpace = cs; - scale = scl; + self = [super init]; + if (self) + { + cache = [[NSMutableArray arrayWithCapacity:CACHE_MAX_SIZE] retain]; + colorSpace = cs; + } + else + { + return nil; + } return self; } -- (void) dealloc +- (void) setColorSpace: (CGColorSpaceRef)cs { - if (context) - CGContextRelease (context); - - if (currentSurface) - CFRelease (currentSurface); + /* We don't need to clear the cache because the new colorspace will + be used next time we create a new context. */ + colorSpace = cs; +} - for (id object in cache) - CFRelease ((IOSurfaceRef)object); +- (void) dealloc +{ + [self releaseSurfaces]; [cache release]; [super dealloc]; } -/* Return the size values our cached data is using. */ -- (NSSize) getSize +- (void) releaseSurfaces { - return size; + [self setContents:nil]; + [self releaseContext]; + + if (currentSurface) + { + CFRelease (currentSurface); + currentSurface = nil; + } + + if (cache) + { + for (id object in cache) + CFRelease ((IOSurfaceRef)object); + + [cache removeAllObjects]; + } +} + + +/* Check whether the current bounds match the IOSurfaces we are using. + If they do return YES, otherwise NO. */ +- (BOOL) checkDimensions +{ + int width = NSWidth ([self bounds]) * [self contentsScale]; + int height = NSHeight ([self bounds]) * [self contentsScale]; + IOSurfaceRef s = currentSurface ? currentSurface + : (IOSurfaceRef)[cache firstObject]; + + return !s || (IOSurfaceGetWidth (s) == width + && IOSurfaceGetHeight (s) == height); } -/* Return a CGContextRef that can be used for drawing to the screen. - This must ALWAYS be paired with a call to releaseContext, and the - calls cannot be nested. */ +/* Return a CGContextRef that can be used for drawing to the screen. */ - (CGContextRef) getContext { - NSTRACE ("[EmacsSurface getContext]"); + CGFloat scale = [self contentsScale]; + + NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "[EmacsLayer getContext]"); + NSTRACE_MSG ("IOSurface count: %lu", [cache count] + (currentSurface ? 1 : 0)); + + if (![self checkDimensions]) + [self releaseSurfaces]; if (!context) { IOSurfaceRef surface = NULL; - - NSTRACE_MSG ("IOSurface count: %lu", [cache count] + (lastSurface ? 1 : 0)); + int width = NSWidth ([self bounds]) * scale; + int height = NSHeight ([self bounds]) * scale; for (id object in cache) { @@ -9667,11 +9549,11 @@ - (CGContextRef) getContext else if (!surface) { int bytesPerRow = IOSurfaceAlignProperty (kIOSurfaceBytesPerRow, - size.width * 4); + width * 4); surface = IOSurfaceCreate - ((CFDictionaryRef)@{(id)kIOSurfaceWidth:[NSNumber numberWithInt:size.width], - (id)kIOSurfaceHeight:[NSNumber numberWithInt:size.height], + ((CFDictionaryRef)@{(id)kIOSurfaceWidth:[NSNumber numberWithInt:width], + (id)kIOSurfaceHeight:[NSNumber numberWithInt:height], (id)kIOSurfaceBytesPerRow:[NSNumber numberWithInt:bytesPerRow], (id)kIOSurfaceBytesPerElement:[NSNumber numberWithInt:4], (id)kIOSurfacePixelFormat:[NSNumber numberWithUnsignedInt:'BGRA']}); @@ -9694,7 +9576,7 @@ - (CGContextRef) getContext (kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host)); - CGContextTranslateCTM(context, 0, size.height); + CGContextTranslateCTM(context, 0, IOSurfaceGetHeight (currentSurface)); CGContextScaleCTM(context, scale, -scale); } @@ -9706,7 +9588,7 @@ - (CGContextRef) getContext IOSurface, so it will be sent to VRAM. */ - (void) releaseContext { - NSTRACE ("[EmacsSurface releaseContextAndGetSurface]"); + NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "[EmacsLayer releaseContext]"); if (!context) return; @@ -9717,19 +9599,34 @@ - (void) releaseContext IOReturn lockStatus = IOSurfaceUnlock (currentSurface, 0, nil); if (lockStatus != kIOReturnSuccess) NSLog (@"Failed to unlock surface: %x", lockStatus); - - /* Put currentSurface back on the end of the cache. */ - [cache addObject:(id)currentSurface]; - lastSurface = currentSurface; - currentSurface = NULL; } -/* Get the IOSurface that we want to draw to the screen. */ -- (IOSurfaceRef) getSurface +- (void) display { - /* lastSurface always contains the most up-to-date and complete data. */ - return lastSurface; + NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "[EmacsLayer display]"); + + if (context) + { + [self releaseContext]; + +#if CACHE_MAX_SIZE == 1 + /* This forces the layer to see the surface as updated. */ + [self setContents:nil]; +#endif + + [self setContents:(id)currentSurface]; + + /* Put currentSurface back on the end of the cache. */ + [cache addObject:(id)currentSurface]; + currentSurface = NULL; + + /* Schedule a run of getContext so that if Emacs is idle it will + perform the buffer copy, etc. */ + [self performSelectorOnMainThread:@selector (getContext) + withObject:nil + waitUntilDone:NO]; + } } @@ -9739,19 +9636,20 @@ - (IOSurfaceRef) getSurface - (void) copyContentsTo: (IOSurfaceRef) destination { IOReturn lockStatus; + IOSurfaceRef source = (IOSurfaceRef)[self contents]; void *sourceData, *destinationData; int numBytes = IOSurfaceGetAllocSize (destination); - NSTRACE ("[EmacsSurface copyContentsTo:]"); + NSTRACE_WHEN (NSTRACE_GROUP_FOCUS, "[EmacsLayer copyContentsTo:]"); - if (!lastSurface || lastSurface == destination) + if (!source || source == destination) return; - lockStatus = IOSurfaceLock (lastSurface, kIOSurfaceLockReadOnly, nil); + lockStatus = IOSurfaceLock (source, kIOSurfaceLockReadOnly, nil); if (lockStatus != kIOReturnSuccess) NSLog (@"Failed to lock source surface: %x", lockStatus); - sourceData = IOSurfaceGetBaseAddress (lastSurface); + sourceData = IOSurfaceGetBaseAddress (source); destinationData = IOSurfaceGetBaseAddress (destination); /* Since every IOSurface should have the exact same settings, a @@ -9759,17 +9657,17 @@ - (void) copyContentsTo: (IOSurfaceRef) destination the other. */ memcpy (destinationData, sourceData, numBytes); - lockStatus = IOSurfaceUnlock (lastSurface, kIOSurfaceLockReadOnly, nil); + lockStatus = IOSurfaceUnlock (source, kIOSurfaceLockReadOnly, nil); if (lockStatus != kIOReturnSuccess) NSLog (@"Failed to unlock source surface: %x", lockStatus); } #undef CACHE_MAX_SIZE -@end /* EmacsSurface */ +@end /* EmacsLayer */ -#endif +#endif /* NS_IMPL_COCOA */ #ifdef NS_IMPL_GNUSTEP