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)?
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
-
other-window
when you meant "frames" andother-frame
?) – phils Nov 13 '14 at 22:16set-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