ㅈㅅㄹ

사실 Emacs를 frame 하나만 띄워서 쓰다보면 버퍼 저글링(juggling)을 심하게 해야 할 때 좀 귀찮은 구석이 있다. 게다가 여러개의 모니터를 사용할 경우 frame을 여러개 띄워 놓고 쓰고 싶긴 하지만 뭐랄까 frame간 이동이 귀찮아 지는 문제가 있다. 원래는 frame간에 이동할 때 C-x <left>이나 C-x <right>previous-frame, next-frame으로 이동하면서 썼지만 아무래도 window 이동은 windmove키를 쓰고 frame간 이동은 별도의 키를 쓰는 것이 그다지 맘에 들지 않았다.


그래서 당분간은 windmove의 좌우 이동키로 frame간에도 이동할 수 있도록 아래와 같이 짜서 사용하기도 했지만, 당시에는 frame-parameter 함수의 존재를 몰라, frame의 geometry 정보를 못 가져 오는 줄 알아서 사실상 키만 windmove키를 사용할 뿐이지 어차피 previous-frame, next-frame에 의존적이라 실제 프레임의 위치에 따라 직관적으로 이동하는 방법은 아니었다.

;; To use multi-monitor more usefully
(defun my:move-to-window-or-frame-left (&optional arg)
  (interactive "P")
  (let ((other-window (windmove-find-other-window 'left arg nil)))
    (if (and (display-graphic-p)
             (null other-window)
             (framep (next-frame)))
        (progn
          (select-frame-set-input-focus (next-frame))
          (unless (window-at-side-p (selected-window) 'right)
            (select-window (car (window-at-side-list (selected-frame) 'right)))))
      (windmove-left arg))))

(defun my:move-to-window-or-frame-right (&optional arg)
  (interactive "P")
  (let ((other-window (windmove-find-other-window 'right arg nil)))
    (if (and (display-graphic-p)
             (null other-window)
             (framep (previous-frame)))
        (progn
          (select-frame-set-input-focus (previous-frame))
          (unless (window-at-side-p (selected-window) 'left)
            (select-window (car (window-at-side-list (selected-frame) 'left)))))
      (windmove-right arg))))

뭐 다소 불편함이 있긴 했지만 위의 함수로 어쨌든 듀얼 모니터 환경에서는 쓸만하긴 했지만 그래도 직관성이 떨어진다거나 모니터가 더 많아지거나 위치가 좌우가 아닌 경우에 대해서는 아쉬움이 있긴 했다.


그러던 와중 Emacswiki를 뒤지다가 framemove.el을 찾아 냈는데, 입력하는 화살표 위치에 있는 frame으로 이동시키는 직관적인 인터페이스를 가지고 있더라. (사실 frame-parameter라는 함수가 존재하는지도 이걸 보기 전엔 몰랐다 -_-) framemove-hook-into-windmove을 non-nil로 설정하면 windmove의 키로 frame간 이동도 seamless하게 연동되어 아주 만족스럽게 쓰고 있다. 솔직히 위에 저것도 그냥 잘 찾아서 framemove.el을 미리 발견했으면 좋았을 껄 괜히 짜서 썼다는 생각마저 들기도 한다 ㅋㅋ


framemove.el의 주석에도 적혀 있지만, 설치는 아래와 같이 하면 되고,

(require 'framemove)
(framemove-default-keybindings)

windmove키와 연동되게 하려면

(require 'framemove)
(windmove-default-keybindings)
(setq framemove-hook-into-windmove t)

를 init script에 넣어 주면 된다.


쓰면서 발견한 한 가지 문제점은 frame이 전환되었을 때 이전에 focus를 가지고 있던 window가 여전히 선택되어 있는 문제인데, 다시 말해 오른쪽의 frame에 가장 오른쪽 window를 선택해 둔 상태에서 왼쪽 frame을 click한 다음 windmove 오른쪽 이동 키를 사용해서 오른쪽 frame으로 진입하게 될 경우, focus가 진입방향인 왼쪽이 아닌 여전히 오른쪽 window에 맞춰져 있는 현상이다. 사소하다면 사소하지만 은근히 내가 원하는 동작과는 좀 취향이 맞지 않아서 위쪽 내 코드에도 진입 방향의 첫번째 window를 선택하도록 하는 코드를 집어 넣었었다. framemove에서도 진입방향의 첫번째 window를 선택하도록 하려면 아래의 advice를 추가하면 된다.

(defun my:fm-next-frame (dir)
  "Select window at direction where the focus entered from."
  (let* ((opposite '((up . bottom) (left . right) (right . left) (down . top)))
         (from (cdr (assoc dir opposite))))
    (unless (window-at-side-p (selected-window) from)
      (select-window (car (window-at-side-list (selected-frame) from))))))
(advice-add 'fm-next-frame :after #'my:fm-next-frame)