4

At work, I have a dual-monitor setup and, naturally, both of them are fullscreened to emacs. I have bindings to switch between the two frames with other-frame, but sometimes I just want particular content on the screen directly in front of me. (One is off to the side.) How can I rotate window configurations among frames (in this case, effectively swapping them)?

  • To clarify, you want to keep the window configurations themselves, but change the frame in which each window configuration is displayed? (I believe you've confused things by referring to "windows" and other-window when you meant "frames" and other-frame?) – phils Nov 13 '14 at 22:16
  • @phils Oh yes, my mistake. Long day. – Sean Allred Nov 13 '14 at 22:27
  • 1
    FWIW, I think this would ideally be dealt with at the window manager level; as it's a problem which can apply to more than just Emacs. (Not that an Emacs-specific solution isn't useful too, mind.) – phils Nov 13 '14 at 22:39
  • 2
    How about just using set-frame-position and change the positions of both frames after gathering the current values? gnu.org/software/emacs/manual/html_node/elisp/… – lawlist Nov 14 '14 at 0:36
  • @lawlist That's another way of thinking about it :) – Sean Allred Nov 14 '14 at 0:37
6

Try swapping window states of root windows of the frames in question:

(defun swap-frames (a b)
  "Swap window states of root windows of two frames."
  (let ((state-a (window-state-get (frame-root-window a)))
        (state-b (window-state-get (frame-root-window b))))
  (window-state-put state-a (frame-root-window b))
  (window-state-put state-b (frame-root-window a))))

(defun swap-two-frames ()
  "Swap states of the first two frames in frame-list."
  (interactive)
  (swap-frames (nth 0 (frame-list)) (nth 1 (frame-list))))

Here's one way to generalize this to implement cyclic rotation of window states.

(defun apply-window-states (states windows)
  "Apply states in STATES to all WINDOWS by calling
`window-state-put'."
  (when (< (length states) (length windows))
    (error "not enough window states to apply to all windows"))
  (when windows
    (window-state-put (car states) (car windows))
    (apply-window-states (cdr states) (cdr windows))))

This helper function just calls window-state-put for all pairs of provided states and windows.

(There must be a better way to do this. Does Emacs Lisp have zip? If it does, we could just map over the result of zipping lists of states and windows and remove this function.)

Now I can define

(defun rotate-frames (arg)
  "Cyclically rotate window states through all frames.
Use the prefix argument to switch direction."
  (interactive "P")
  (let* ((windows (mapcar (lambda (f) (frame-root-window f)) (frame-list)))
         (states (mapcar (lambda (w) (window-state-get w)) windows)))
    (if (not arg)
        ;; cycle in one direction
        (progn
          (apply-window-states (cdr states) (butlast windows))
          (window-state-put (car states) (car (last windows))))
      ;; cycle in the other direction
      (apply-window-states (butlast states) (cdr windows))
      (window-state-put (car (last states)) (car windows)))))

Finally, set some key bindings.

(global-set-key (kbd "S-<right>") #'rotate-frames)
(global-set-key (kbd "S-<left>") (lambda () (interactive) (rotate-frames t)))

I like it. I might actually use this myself. :-)

  • Is there any way to extend this to rotate states through all frames? – Sean Allred Nov 13 '14 at 22:26
  • @SeanAllred: Yes, certainly. I'll update the answer when I find a moment later today. – Constantine Nov 13 '14 at 22:29
  • @SeanAllred: I updated the answer. (I think it's pretty neat!) – Constantine Nov 14 '14 at 3:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.