Subject: bug#74750: closed (Re: bug#74750: clone-frame and make-frame pixelwise issues)

From: [email-masked] (GNU bug Tracking System)
Date: Mon, 23 Dec 2024 18:01:07 +0000

Your bug report

#74750: clone-frame and make-frame pixelwise issues

which was filed against the emacs package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
74750: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=74750
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#74750: clone-frame and make-frame pixelwise issues
From: martin rudalics <[email-masked]>
To: Ship Mints <[email-masked]>
Cc: Eli Zaretskii <[email-masked]>, [email-masked]
Date: Mon, 23 Dec 2024 19:00:05 +0100
List-Id: <debbugs-submit.debbugs.gnu.org>

 > Please close this, and thank you. P.S. I have not yet tested this on GTK
 > but will after the new year when back at a Linux box on which I have GTK.

Bug closed.

Thanks, martin

Subject: clone-frame and make-frame pixelwise issues
From: Ship Mints <[email-masked]>
To: [email-masked]
Date: Mon, 9 Dec 2024 10:51:23 -0500
List-Id: <debbugs-submit.debbugs.gnu.org>

While trying to reconcile pixelwise frame sizing behaviors, I narrowed down
two related issues.

clone-frame does not correctly clone frames on a pixelwise basis.

make-frame's text-pixels geometry support does not produce specified
pixelwise geometry. This also impacts frameset-restore's ability to
precisely reproduce pixelwise frame sizes.

I consider these to be related as clone-frame's use of make-frame could be
using text-pixels but if that doesn't work then pixelwise cloning won't
work. I did read through the code base as best as I could but could not
find the source of the text-pixels issue.

The following reproducer, under -Q, shows the same results on 29.4 and
30.0.92. My main platform is NS and I also did some testing on GTK. GTK's
issues seem a bit "messier" and I didn't spend any time trying to
understand them in depth as I was more interested to know if GTK worked
correctly or not, which it doesn't.

(switch-to-buffer "*Messages*")
(let ((target-text-width 1700)
      (target-text-height 1000)
      (native-width)
      (native-height)
      (msg (lambda (s frame)
             (message "%s text-width=%d (Δ%d) text-height=%d (Δ%d)
native-width=%d (Δ%d) native-height %d (Δ%d)\n"
                      s
                      (frame-text-width frame) (- (frame-text-width frame)
target-text-width)
                      (frame-text-height frame) (- (frame-text-height
frame) target-text-height)
                      (frame-native-width frame) (- (frame-native-width
frame) native-width)
                      (frame-native-height frame) (- (frame-native-height
frame) native-height)))))
  (set-frame-position nil 0 0)
  (set-frame-size nil target-text-width target-text-height 'pixelwise)
  (setq native-width (frame-native-width)
        native-height (frame-native-height))
  (message "Targets: text-width=%d text-height=%d\n" target-text-width
target-text-height)
  (funcall msg "orig" (selected-frame))

  (message "clone-frame under frame-resize-pixelwise nil; expectation: use
lines/columns geometry; outcome: met")
  (let ((frame-resize-pixelwise nil))
    (let ((new-frame (clone-frame)))
      (funcall msg "new" new-frame)
      (delete-frame new-frame)))

  (message "clone-frame under frame-resize-pixelwise t; expectation:
pixelwise geometry; outcome: unmet")
  (let ((frame-resize-pixelwise t))
    (let ((new-frame (clone-frame)))
      (funcall msg "new" new-frame)
      (delete-frame new-frame)))

  (message "clone-frame followed by manual resize; expectation: pixelwise
geometry; outcome: met (but two steps)")
  (let ((new-frame (clone-frame)))
    (set-frame-size new-frame target-text-width target-text-height
'pixelwise)
    (funcall msg "new" new-frame)
    (delete-frame new-frame))

  (message "manual clone under frame-resize-pixelwise using text-pixels;
expectation: pixelwise geometry; outcome: unmet")
  ;; code lifted from clone-frame
  ;; incorrect width offset seems to be equal to frame-scroll-bar-width
  (let* ((frame-resize-pixelwise t)
         (frame (selected-frame))
         (no-windows nil)
         (windows (unless no-windows
                    (window-state-get (frame-root-window frame))))
         (default-frame-alist
          (seq-remove (lambda (elem)
                        (memq (car elem) frame-internal-parameters))
                      (frame-parameters frame)))
         (new-frame))
    (when (and (display-graphic-p frame) frame-resize-pixelwise)
      (push (cons 'width (cons 'text-pixels (frame-text-width frame)))
default-frame-alist)
      (push (cons 'height (cons 'text-pixels (frame-text-height frame)))
default-frame-alist))
    (setq new-frame (make-frame))
    (when windows
      (window-state-put windows (frame-root-window new-frame) 'safe))
    (unless (display-graphic-p frame)
      (select-frame new-frame))
    (funcall msg "new" new-frame)
    (delete-frame new-frame)))

This is an implementation of clone-frame that uses text-pixels under
make-frame. This depends on make-frame text-pixels being corrected. Happy
to supply this as a patch should the discussion of these issues progress in
that direction.

(defun clone-frame (&optional frame no-windows pixelwise)
  "Make a new frame with the same parameters and windows as FRAME.
With a prefix arg NO-WINDOWS, don't clone the window configuration.  When
PIXELWISE is non-nil or if `frame-resize-pixelwise' is non-nil, and frame
is not text-only, clone the originating frame's pixel size.

FRAME defaults to the selected frame.  The frame is created on the
same terminal as FRAME.  If the terminal is a text-only terminal then
also select the new frame."
  (interactive (list (selected-frame) current-prefix-arg))
  (let* ((frame (or frame (selected-frame)))
         (windows (unless no-windows
                    (window-state-get (frame-root-window frame))))
         (default-frame-alist
          (seq-remove (lambda (elem)
                        (memq (car elem) frame-internal-parameters))
                      (frame-parameters frame)))
         (new-frame))
    (when (and (display-graphic-p frame)
               (or pixelwise frame-resize-pixelwise))
      (push (cons 'width (cons 'text-pixels (frame-text-width frame)))
            default-frame-alist)
      (push (cons 'height (cons 'text-pixels (frame-text-height frame)))
            default-frame-alist))
    (setq new-frame (make-frame))
    (when windows
      (window-state-put windows (frame-root-window new-frame) 'safe))
    (unless (display-graphic-p frame)
      (select-frame new-frame))
    new-frame))

-Stephane