branch: externals/bufferlo commit 1aaac07568c631a0e28c9cf8fc8c56efa2f8030f Author: shipmints <shipmi...@gmail.com> Commit: shipmints <shipmi...@gmail.com>
Refine session frameset-restore and frame geometry handling This introduces the user options bufferlo-frameset-restore-function, bufferlo-frameset-restore-parameters-function with default implementations. frameset-restore behavior is very window-manager specific; e.g., macOS vs. GNOME behaviors differ materially. --- bufferlo.el | 174 +++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 113 insertions(+), 61 deletions(-) diff --git a/bufferlo.el b/bufferlo.el index 641389313e..6226e658fc 100644 --- a/bufferlo.el +++ b/bufferlo.el @@ -608,6 +608,28 @@ packages that do, and you want to ensure they are filtered in advance of restoring bufferlo framesets." :type '(repeat symbol)) +(defcustom bufferlo-frameset-restore-function #'bufferlo-frameset-restore-default + "Function to restore a frameset, which see `frameset-restore'. +It defaults to `bufferlo-frameset-restore-default'. + +The function accepts a single parameter, the `frameset' to restore." + :type 'function) + +(defcustom bufferlo-frameset-restore-parameters-function #'bufferlo-frameset-restore-parameters-default + "Function to create parameters for `frameset-restore', which see. + +The function should create a plist of the form: + + (list :reuse-frames value + :force-display value + :force-onscreen value + :cleanup-frames value) + +where each property is as documented by `frameset-restore'. + +It defaults to `bufferlo-frameset-restore-parameters-default'." + :type 'function) + (defcustom bufferlo-frame-geometry-function #'bufferlo-frame-geometry-default "Function to produce a bufferlo-frame-geometry alist. It defaults to `bufferlo-frame-geometry-default'. @@ -1326,6 +1348,7 @@ If INVERT is non-nil, return the non-exclusive buffers instead." this-bufs))) (defun bufferlo--kill-buffer-forced (buffer) + "Forcibly kill BUFFER, even if modified." (let ((kill-buffer-query-functions nil)) (with-current-buffer buffer (set-buffer-modified-p nil) @@ -2226,9 +2249,7 @@ the message after successfully restoring the bookmark." (display-graphic-p) (eq bufferlo-bookmark-frame-load-make-frame 'restore-geometry)) (when-let* ((fg (alist-get 'bufferlo--frame-geometry bookmark))) - (let-alist fg - (set-frame-position nil .left .top) - (set-frame-size nil .width .height 'pixelwise)))) + (bufferlo--set-frame-geometry fg))) (raise-frame)) @@ -2353,16 +2374,36 @@ CANDIDATES are the prompt options to select." )) (defun bufferlo-frame-geometry-default (frame) - "Produce an alist for FRAME's geometry. + "Produce an alist for FRAME pixel-level geometry. The alist is of the form: + ((left . pixels) (top . pixels) (width . pixels) - (height . pixels))" - `((left . ,(frame-parameter frame 'left)) - (top . ,(frame-parameter frame 'top)) - (width . ,(frame-text-width frame)) - (height .,(frame-text-height frame)))) + (height . pixels)) + +Return nil if no pixel-level geometry is available; for example, if the +display is a tty." + (if (display-graphic-p frame) + `((left . ,(frame-parameter frame 'left)) + (top . ,(frame-parameter frame 'top)) + (width . ,(frame-text-width frame)) + (height .,(frame-text-height frame))) + nil)) + +(defun bufferlo--set-frame-geometry (frame-geometry &optional frame) + "Set FRAME-GEOMETRY as produced by `bufferlo-frame-geometry-default'. +Geometry set for FRAME or the current frame, if nil." + ;; Some window managers need an extra display cycle for frame + ;; changes to take effect from Emacs's perspective, so we add + ;; needed redisplay calls. + (setq frame (or frame (selected-frame))) + (let-alist frame-geometry + (when (and .left .top .width .height) ; defensive in case geometry stored from a tty + (set-frame-position nil .left .top) + (redisplay) + (set-frame-size nil .width .height 'pixelwise) + (redisplay)))) (defvar bufferlo--active-sessions nil "Global active bufferlo sessions. @@ -2390,6 +2431,33 @@ FRAMESET is a bufferlo-filtered `frameset'." 'handler #'bufferlo--bookmark-session-handler) bookmark-record)) +(defun bufferlo-frameset-restore-parameters-default () + "Function to create parameters for `frameset-restore', which see." + (list :reuse-frames nil + :force-display t + :force-onscreen (display-graphic-p) + :cleanup-frames nil)) + +(defun bufferlo-frameset-restore-default (frameset) + "Invoke `frameset-restore' with FRAMESET, which see." + (let ((params (funcall bufferlo-frameset-restore-parameters-function)) + (default-frame-alist)) + (with-temp-buffer + (when (ignore-errors + (frameset-restore frameset + :filters + (when (memq bufferlo-frameset-restore-geometry '(bufferlo nil)) + (let ((filtered-alist (copy-tree frameset-persistent-filter-alist))) + (mapc (lambda (sym) (setf (alist-get sym filtered-alist) :never)) + (seq-union bufferlo--frameset-restore-filter bufferlo-frameset-restore-filter)) + filtered-alist)) + :reuse-frames (plist-get params :reuse-frames) + :force-display (plist-get params :force-display) + :force-onscreen (plist-get params :force-onscreen) + :cleanup-frames (plist-get params :cleanup-frames)) + t) ; frameset-restore returns neither a status nor a list of restored frames + )))) + (defun bufferlo--bookmark-session-handler (bookmark-record &optional no-message) "Handle bufferlo session bookmark. The argument BOOKMARK-RECORD is the to-be restored session bookmark created via @@ -2403,63 +2471,48 @@ the message after successfully restoring the bookmark." (message "Bufferlo session %s is already active" bookmark-name) (if (> (length active-bookmark-names) 0) (message "Close or clear active bufferlo bookmarks: %s" active-bookmark-names) - (let* ((tabsets-str (bookmark-prop-get bookmark-record 'bufferlo-tabsets)) - (tabsets)) + (let ((tabsets-str (bookmark-prop-get bookmark-record 'bufferlo-tabsets)) + (tabsets)) (if (not (readablep tabsets-str)) (message "Bufferlo session bookmark %s: unreadable tabsets" bookmark-name) (setq tabsets (car (read-from-string tabsets-str))) - (let ((first-tab-frame t)) - (dolist (tab-group tabsets) - (when (or (not first-tab-frame) - (and first-tab-frame (not bufferlo-session-restore-tabs-reuse-init-frame))) - (with-temp-buffer - (select-frame (make-frame)))) - ;; (lower-frame) ; attempt to reduce visual flashing - (when-let* ((fg (alist-get 'bufferlo--frame-geometry tab-group))) - (when (and - (display-graphic-p) - (memq bufferlo-session-restore-geometry-policy '(all tab-frames)) - (or (not first-tab-frame) - (and first-tab-frame (eq bufferlo-session-restore-tabs-reuse-init-frame 'reuse-reset-geometry)))) - (let-alist fg - (set-frame-position nil .left .top) - (set-frame-size nil .width .height 'pixelwise)))) - (when-let* ((tbm-names (alist-get 'bufferlo--tbms tab-group))) - (let ((bufferlo-bookmark-tab-replace-policy 'replace) ; we handle making tabs in this loop - (tab-bar-new-tab-choice t) - (first-tab (or - (not first-tab-frame) - (and first-tab-frame (not bufferlo-session-restore-tabs-reuse-init-frame))))) - (dolist (tbm-name tbm-names) - (unless first-tab - (tab-bar-new-tab-to)) - (bufferlo--bookmark-jump tbm-name) - (setq first-tab nil)))) - (setq first-tab-frame nil)) - (raise-frame)))) - (let* ((frameset-str (bookmark-prop-get bookmark-record 'bufferlo-frameset)) - (frameset)) + (when tabsets ; could be readable and nil + (let ((first-tab-frame t)) + (dolist (tab-group tabsets) + (when (or (not first-tab-frame) + (and first-tab-frame (not bufferlo-session-restore-tabs-reuse-init-frame))) + (with-temp-buffer + (select-frame (make-frame)))) + ;; (lower-frame) ; attempt to reduce visual flashing + (when-let* ((fg (alist-get 'bufferlo--frame-geometry tab-group))) + (when (and + (display-graphic-p) + (memq bufferlo-session-restore-geometry-policy '(all tab-frames)) + (or (not first-tab-frame) + (and first-tab-frame (eq bufferlo-session-restore-tabs-reuse-init-frame 'reuse-reset-geometry)))) + (bufferlo--set-frame-geometry fg))) + (when-let* ((tbm-names (alist-get 'bufferlo--tbms tab-group))) + (let ((bufferlo-bookmark-tab-replace-policy 'replace) ; we handle making tabs in this loop + (tab-bar-new-tab-choice t) + (first-tab (or + (not first-tab-frame) + (and first-tab-frame (not bufferlo-session-restore-tabs-reuse-init-frame))))) + (dolist (tbm-name tbm-names) + (unless first-tab + (tab-bar-new-tab-to)) + (bufferlo--bookmark-jump tbm-name) + (setq first-tab nil)))) + (setq first-tab-frame nil)) + (raise-frame))))) + (let ((frameset-str (bookmark-prop-get bookmark-record 'bufferlo-frameset)) + (frameset)) (if (not (readablep frameset-str)) (message "Bufferlo session bookmark %s: unreadable frameset" bookmark-name) (setq frameset (car (read-from-string frameset-str))) (if (and frameset (not (frameset-valid-p frameset))) (message "Bufferlo session bookmark %s: invalid frameset" bookmark-name) - (when (ignore-errors - (with-temp-buffer - (let ((default-frame-alist) - (inhibit-redisplay t)) - (frameset-restore frameset - :filters - (when (memq bufferlo-frameset-restore-geometry '(bufferlo nil)) - (let ((filtered-alist (copy-tree frameset-persistent-filter-alist))) - (mapc (lambda (sym) (setf (alist-get sym filtered-alist) :never)) - (seq-union bufferlo--frameset-restore-filter bufferlo-frameset-restore-filter)) - filtered-alist)) - :reuse-frames nil - :force-display t - :force-onscreen (display-graphic-p) - :cleanup-frames nil))) - t) + (when frameset ; could be readable and nil + (funcall bufferlo-frameset-restore-function frameset) (dolist (frame (frame-list)) (with-selected-frame frame (when (frame-parameter nil 'bufferlo--frame-to-restore) @@ -2473,9 +2526,8 @@ the message after successfully restoring the bookmark." (when (and (display-graphic-p frame) (memq bufferlo-session-restore-geometry-policy '(all frames))) - (let-alist (frame-parameter nil 'bufferlo--frame-geometry) - (set-frame-position nil .left .top) - (set-frame-size nil .width .height 'pixelwise))) + (when-let* ((fg (frame-parameter nil 'bufferlo--frame-geometry))) + (bufferlo--set-frame-geometry fg))) (set-frame-parameter nil 'bufferlo--frame-to-restore nil)) (raise-frame)))))) (push