Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fullscreen toggle for all Emacs buffers #89

Open
minad opened this issue Nov 29, 2024 · 7 comments
Open

Fullscreen toggle for all Emacs buffers #89

minad opened this issue Nov 29, 2024 · 7 comments

Comments

@minad
Copy link
Member

minad commented Nov 29, 2024

A general fullscreen toggle would be nice to have. I have this in my config right now, but this likely misses many edge cases and might not be the best solution.

(defvar +exwm-fullscreen--frame nil)
(defvar +exwm-fullscreen--state nil)

(defun +exwm-fullscreen ()
  (interactive)
  (if (eq major-mode 'exwm-mode)
      (exwm-layout-toggle-fullscreen)
    (if +exwm-fullscreen--frame
        (progn
          (tab-bar-mode 1)
          (delete-frame +exwm-fullscreen--frame)
          (buffer-local-restore-state +exwm-fullscreen--state)
          (setq +exwm-fullscreen--state nil
                +exwm-fullscreen--frame nil))
      (setq +exwm-fullscreen--state
            (buffer-local-set-state mode-line-format nil
                                    tab-bar-format nil
                                    tab-line-format nil)
            +exwm-fullscreen--frame
            (make-frame '((minibuffer . nil))))
      (let ((geo (frame-monitor-geometry)))
        (set-frame-size +exwm-fullscreen--frame (nth 2 geo) (nth 3 geo) t)))))
@minad
Copy link
Member Author

minad commented Dec 1, 2024

More sophisticated version:

(defvar +exwm-fullscreen--restore nil)

(defun +exwm-fullscreen--change (&rest _)
  (when +exwm-fullscreen--restore
    (+exwm-fullscreen)))

(defun +exwm-fullscreen--enter ()
  ;; Create frame and resize
  (let ((frame (make-frame '((minibuffer . nil)
                             (fullscreen . fullboth)))))
    (push (lambda () (delete-frame frame)) +exwm-fullscreen--restore)
    (let ((geo (frame-monitor-geometry)))
      (set-frame-size frame (nth 2 geo) (nth 3 geo) t)))
  ;; Disable tab-bar-mode
  (when tab-bar-mode
    (push #'tab-bar-mode +exwm-fullscreen--restore)
    (tab-bar-mode -1))
  ;; Adjust buffer-local variables
  (dolist (var '(mode-line-format tab-line-format))
    (let ((buf (current-buffer))
          (orig (symbol-value var))
          (local (local-variable-p var)))
      (set (make-local-variable var) nil)
      (push (lambda ()
              (when (buffer-live-p buf)
                (with-current-buffer buf
                  (if local
                      (kill-local-variable var)
                    (set var orig)))))
            +exwm-fullscreen--restore)))
  ;; Special mode tricks
  (when (and (eq major-mode 'pdf-view-mode) (fboundp 'pdf-view-fit-page-to-window))
    (pdf-view-fit-page-to-window))
  ;; Ensure that buffer change leaves fullscreen
  (run-at-time
   0.1 nil
   (lambda (buf)
     (when +exwm-fullscreen--restore
       (add-hook 'window-selection-change-functions #'+exwm-fullscreen--change nil 'local)
       (add-hook 'window-buffer-change-functions #'+exwm-fullscreen--change nil 'local)
       (push (lambda ()
               (when (buffer-live-p buf)
                 (with-current-buffer buf
                   (remove-hook 'window-selection-change-functions #'+exwm-fullscreen--change 'local)
                   (remove-hook 'window-buffer-change-functions #'+exwm-fullscreen--change 'local))))
             +exwm-fullscreen--restore)))
   (current-buffer)))

(defun +exwm-fullscreen ()
  (interactive)
  (cond
   ;; Leave fullscreen for Emacs buffer
   (+exwm-fullscreen--restore
    (mapc #'funcall +exwm-fullscreen--restore)
    (setq +exwm-fullscreen--restore nil))
   ;; EXWM buffer fullscreen toggle
   ((eq major-mode 'exwm-mode)
    (exwm-layout-toggle-fullscreen))
   ;; Enter fullscreen for Emacs buffer
   (t (+exwm-fullscreen--enter))))

@ezemtsov
Copy link

ezemtsov commented Dec 2, 2024

A proper full-screen would be great!
I had a tiny configuration for maximizing the selected buffer bound to s-f:

(defvar fullscreen-buffer--state nil)

(defun fullscreen-buffer--toggle ()
  "Maximize buffer"
  (interactive)
  (if fullscreen-buffer--state
      (let ((val (get-register (tab-bar--current-tab-index))))
        (register-val-jump-to val nil)
        (tab-bar-mode t)
        (setq mode-line-format (default-value 'mode-line-format))
        (setq fullscreen-buffer--state nil))
    (progn
      (window-configuration-to-register (tab-bar--current-tab-index))
      (delete-other-windows)
      (tab-bar-mode -1)
      (setq mode-line-format nil)
      (setq fullscreen-buffer--state t))))

Testing your config, looks like it correctly enables full-screen for both EXWM and normal buffers, but there seem to be some issues:

  1. Toggling back only works on EXWM buffers, unclear how disable fullscreen emacs buffer frame

@lrustand
Copy link

lrustand commented Dec 7, 2024

I also had a similar function in my config:

(defun my-toggle-fullscreen ()
  "Toggle fullscreen for the current buffer.
Automatically exits fullscreen if any window-changing command is executed."
  (interactive)
  (if (= 1 (length (window-list)))
      (when my-fullscreen-window-configuration
        (set-window-configuration my-fullscreen-window-configuration)
        (setq my-fullscreen-window-configuration nil)
        (advice-remove 'split-window #'my-exit-fullscreen-advice))
    (setq my-fullscreen-window-configuration (current-window-configuration))
    (delete-other-windows)
    (advice-add 'split-window :before #'my-exit-fullscreen-advice)))

I think a builtin function would be great.

@minad
Copy link
Member Author

minad commented Dec 8, 2024

@ezemtsov

Testing your config, looks like it correctly enables full-screen for both EXWM and normal buffers, but there seem to be some issues: Toggling back only works on EXWM buffers, unclear how disable fullscreen emacs buffer frame

In my config I've bound s-f to +exwm-fullscreen. s-f works fpr toggling both normal and EXWM buffers.

@ezemtsov
Copy link

@minad just tried again after putting in a dedicated file with lexical-binding, works fine. Was tricked again by dynamic scoping by default.

More comments:

  1. The system tray is still displayed even on buffers that are displayed in full-screen
  2. Calling M-x forces an emacs buffer to exit fullscreen, is this desired?
  3. Creating another frame causes flickering (possibly because I don't use a compositor). Deleting other windows in comparison feels smoother.

Another concern is how this would play with the idea discussed earlier here about using tabs for workspaces and frames per monitors. I have a function that automatically gets a list of connected outputs from xrandr and ensures that each connected screen has a frame on exwm-randr-screen-change-hook. If we are using frames for full screen mode, would this become an issue?

@minad
Copy link
Member Author

minad commented Dec 11, 2024

  1. Yes. I don't know how to hide the systray, since I am not using an external one - the Emacs tab bar is my system status bar. It should be possible to mark the frame as fullscreen. EDIT: I've updated the snippet above. The new frame is created with the fullscreen frame parameter.
  2. Yes, this is intended.
  3. Creating another frame is the better option if one wants to hide the echo area. It also leads to a better isolation, in contrast to modifying the already existing frame.

Another concern is how this would play with the idea discussed earlier here about using tabs for workspaces and frames per monitors. I have a function that automatically gets a list of connected outputs from xrandr and ensures that each connected screen has a frame on exwm-randr-screen-change-hook. If we are using frames for full screen mode, would this become an issue?

I think it should work well with the tab-bar idea for workspaces. As you said, we will anyway have to continue to support multiple Emacs frames anyway because of multiple monitors. The question is if it leads to complications if we continue to allow multiple frames on a single monitor.

@ezemtsov
Copy link

The question is if it leads to complications if we continue to allow multiple frames on a single monitor.

This is exactly what I was thinking of. The model "one monitor - one frame" could potentially simplify the logic around automatically enabling external monitors, which is great for laptops, especially when a user is connecting it to different external gear with unpredictable output names (home/office/clients etc.)

This is what I have currently:

;; Attempt to ensure correct external monitor handling

(defun +exwm-ensure-workspaces (num-workspaces)
  "Ensure there are exactly NUM-WORKSPACES created in EXWM"
  (let ((current-num-workspaces (length exwm-workspace--list)))
    (when (< current-num-workspaces num-workspaces)
      (dotimes (_ (- num-workspaces current-num-workspaces))
        (exwm-workspace-add))
      (message "Created %d new workspaces" (- num-workspaces current-num-workspaces)))
    (when (> current-num-workspaces num-workspaces)
      (dotimes (i (- current-num-workspaces num-workspaces))
        (exwm-workspace-delete))
      (message "Deleted %d workspaces" (- current-num-workspaces num-workspaces)))))

(defun +exwm-xrandr-list ()
  "xrandr query to get a list of monitors"
  (split-string (shell-command-to-string "xrandr --listmonitors | awk '{print $4}'") "\n" t))

(defun +exwm-update-workspaces ()
  "Ensure every connected monitor has a dedicated EXWM workspace"
  (let* ((monitors (+exwm-xrandr-list))
         (num-monitors (length monitors)))
    ;; Ensure enough workspaces exist
    (+exwm-ensure-workspaces num-monitors)
    ;; Clear current RANDR configuration
    (setq exwm-randr-workspace-monitor-plist nil)
    (dotimes (i num-monitors)
      ;; Assign a workspace to each monitor
      (setq exwm-randr-workspace-monitor-plist
            (append exwm-randr-workspace-monitor-plist
                    (list i (nth i monitors)))))
    (+exwm-ensure-workspaces num-monitors)
    (message "Updated workspaces for monitors: %s" monitors)))


(add-hook 'exwm-randr-refresh-hook #'+exwm-update-workspaces)
(add-hook 'exwm-randr-screen-change-hook #'exwm-randr-refresh)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants