Emacsでアクセサリを表示してみたくなった

目次

1 はじめに

Emacsで表示可能な美人時計のELISPの存在を知ったのですが、常用しようとすると 意外とウインドウ操作の影響を受けてしまって、これじゃ役に立たないじゃん! と思い、どうにかしようとあれこれ試した記録です。

2 Emacsでのウインドウ操作について

Emacsでは画面全体をフレームと呼び、その中をウインドウという単位でタイル状に 分割し、そのウインドウにバッファと呼ばれるデータの実体を表示します。 ウインドウは上下に分割したり左右に分割したり大きさを変えたりと、フレーム内では 自由に変更できます。例えば左右に分割して二つの異なるファイル(バッファ)を並べて 表示して見比べたり、上下に分割して一つのファイル(バッファ)の行頭と行末を 同時に表示したりと便利に利用できます。しかしその反面、分割してたのを止めて 一つのウインドウに戻したり、新しいファイルを開く場合などはウインドウレイアウトが 意図せず変更される場合があります。この為、美人時計のようなアクセサリ類のウインドウ を良い感じにずっと表示し続ける事は、至難の業という事に改めて気づきました。

そんな訳で、ウインドウの分割や消去といった操作はそのままに、どうすれば良い感じに アクセサリ類ウインドウを表示できるか考えてみる事にしました。

3 ウインドウ操作を都合の良いように改良してみる

3.1 ウインドウ表示を維持できないのは何故?

TANEが良く使うデフォルトのウインドウ操作はおおよそ以下のものです。

  • split-window-below (C-x 2) 現在フォーカスしているウインドウを上下に分割する
  • split-window-horizontally (C-x 3) 現在フォーカスしているウインドウを左右に分割する
  • other-window (C-x o) フォーカスするウインドウを切り替える
  • delete-window (C-x 0) 現在のウインドウを閉じる(消去する)
  • delete-other-windows (C-x 1) フォーカスしているウインドウ以外のウインドウを閉じる(消去する)

他に、キーバインドを追加しています。

(global-set-key [C-down]  'windmove-down)
(global-set-key [C-up]    'windmove-up)
(global-set-key [C-left]  'windmove-left)
(global-set-key [C-right] 'windmove-right)
(global-set-key [?\C-:]   'shrink-window-horizontally)
(global-set-key "\C-\]"   'enlarge-window-horizontally)
(global-set-key [?\C--]   'shrink-window)
(global-set-key [?\C-^]   'enlarge-window)

アクセサリウインドウ表示を維持するにあたって、先に挙げたデフォルトの ウインドウ操作の、delete-other-windowsは折角表示しているアクセサリウインドウを 閉じてしまう操作ですし、other-windowは表示しているだけのアクセサリウインドウは 選択しなくても良いんですけど?な、ちょっと邪魔臭い操作となってしまいます。

という訳で、ここんところをどうにかすればいいんじゃね? というゆる~い発想 です。

3.2 無視して欲しいって教えてあげないと無理でしょ?

このバッファを表示しているウインドウは、ウインドウ操作の対象にして欲しくない というのを解釈して、ウインドウ切り替え時などに考慮すれば良いんじゃね? と考えました。

delete-windowなどの操作は「/usr/share/emacs/2?.?/lisp/window.el.gz」に 関数の実体が存在します。これを改造する事も考えたのですが、組み込みのC関数を 実行するケースがあり、簡単に改造する事ができそうにありませんでした。 仕方なく、似た動きをするELISPを書く事にしました。

別にELISPに詳しい訳でもなんでもないので、実現できるか否かは神のみぞ知ると いうのがこのときの状況でした(^^;

3.3 ELISPで遊ぶ

やりたい事は単純で、例えばウインドウを切り替える場合は、 「切り替え候補のウインドウが、操作を除外したいバッファを表示してなければ、 そのウインドウに切り替え、そうでなければその次のウインドウを検査する。」 くらいの感じです。ELISP以外のプログラム言語をそれなりに使える方であれば、 判断に必要な情報が揃っていれば、コーディング自体は全く大した事では無いと 感じるのではないでしょうか。ELISPだとなんだか敷居が高い気がする のですが、リストの取り出し方などが判れば、記述がちょっと変わったプログラム 言語ってだけだだと思います。

ちょっと悩んだのはウインドウを開く方。普通に使っていると、今あるウインドウを 再利用する形で新規に開いたバッファを表示したり(例えばlist-buffersとか)、 ウインドウが一つしか無いとウインドウを分割して開いたりと、意外と無秩序に 開いている(本当はルールがあるのかも知れませんが良く判らない(^^;)事に気づきます。 これを乗っ取るのに、windowP-display-buffer-functionという変数に 関数を結び付けて開き方を変える必要がありました。これについては 好みが分かれる感じになりそうですが、色々設定可能にするのも面倒臭いので、 アクセサリウインドウ表示の邪魔にならない最低ラインで譲歩するという事に しました。この為、例えば翻訳サイトにアクセスする text-translator は、翻訳結果の文書量に応じて適切な高さのウインドウを開くような動きを するのですが、そういうのが無しになります。

3.4 そんな訳で作ってみた

できたのが以下のものです。

window-plus-v008.tar.xz

window-plus.elを/usr/share/emacs/site-lisp なんかに突っ込んで、 .emacs(オールドスタイルですみません)とかに

(require 'window-plus)
(setq display-buffer-function windowP-display-buffer-function)
(setq windowP-ignore-buffers (list "*bijin-tokei*" "*wclock*" "*Calendar*" "*emneko*"))
(global-set-key "\eo"   'other-window)
(global-set-key "\C-xo" 'other-window+)
(global-set-key "\e0"   'delete-window)
(global-set-key "\C-x0" 'delete-window+)
(global-set-key "\e1"   'delete-other-windows)
(global-set-key "\C-x1" 'delete-other-windows+)

てな感じの設定を追加して使用します。この例では、 「*bijin-tokei*」「*wclock*」「*Calendar*」「*emneko*」のいずれかの バッファを表示しているウインドウを操作から除外します。 ちょっと試していただくと「そういう事か」と分かっていただけると思います。

元々のキーバインドは「+付き」の関数に置き換えて、元々の「+無し」関数は 別のバインドに移しています。 人によってはバインドが被る可能性がありますが、完全に置き換えると ウインドウフォーカスが急に面倒臭い事になる可能性があります (Calendarは操作したくなりますもんね)ので、どこかしらにバインドを 移す事をお勧めします。

途中でバッファ名の追加や削除を行いたくなった場合は以下の関数で 無効バッファ名リストを更新できます。

関数(コマンド) 説明
winp-show-ignore-buffers 無効バッファ一覧をミニバッファに表示します。
winp-add-ignore-buffer 無効バッファ一覧に追加します。バッファ名を入力してください。
winp-remove-ignore-buffer 無効バッファ一覧から削除します。バッファ名を入力してください。
winp-add-ignore-current-buffer カレントバッファを無効バッファ一覧に追加します。
winp-remove-ignore-current-buffer カレントバッファを無効バッファ一覧から削除します。

例えば、リアルタイムに動作するELISPのデバッグメッセージを「*Messages*」に 出すようにしたが、他の事をやっている間もログ表示を眺めたい場合などに、 一時的にバッファ名を追加し、用事が無くなれば削除するという使い方を想定しています。

ただ、万事OKという感じにはなってないと思います。「あ~、そうなるか~~」 の例を挙げてみます。

[あ~、そうなるか~~の例]

という訳で、良い感じのウインドウレイアウトを一時記録、壊れたらリストアする簡単なバインド を併用しています。

(global-set-key "\C-t\C-s"   '(lambda () ""
                                (interactive)
                                (message "Save window configuration")
                                (setq window-config (current-window-configuration))))
(global-set-key "\C-t\C-l"   '(lambda () ""
                                (interactive)
                                (message "Restore window configuration")
                                (set-window-configuration window-config)))

3.5 その他

ELISPによってはdelete-other-windowsを実行しているものもあります。 例えばcolor-moccur.el は、文字列検索をするのにとても便利なELISPですが、検索結果を別ウインドウに表示する際に delete-other-windowsを実行している為、レイアウトが完全に崩されます。 このようなものはどうしようもありませんので、本体のELISPを改造する(具体的には delete-other-windows実行コードを削除する)必要があるかも知れません。

4 アクセサリELISP

なんとなく表示してても、操作の邪魔にならなくなったという事で拙作を含めて アクセサリELISPを御紹介。因みに、Emacs本体に付属している hanoiや life は ウインドウフォーカスを失うと止まってしまうようなので、アクセサリと してはイマイチ使えないようです。そんな訳で意外とアクセサリELISPは少ない 気がしなくもありません。

4.1 美人時計(bijin-tokei.el)

[美人時計 on Emacs]

原作はsanryuu氏の bijin-tokei.el で、window-plus.elを作成する原動力となった作品です。 それをTANEの都合の良いように改造して使わせてもらってます。

bijin-tokei-161216.tar.xz

画像をウインドウサイズに応じて拡大/縮小表示するようにしました。 image+.elの コードを拝借しています。ImageMagick(もしくはそれに代わる画像縮小外部コマンド)が 必要になっています。また、 emacs-deferred を使用するようにしました。予めパッケージインストールしておく 必要があります。

使用方法は原作に準拠しています。

  • .emacs設定例
;; 原作準拠設定
(require 'bijin-tokei)
(setq bijin-tokei-genre "jp")

bijin-tokei-genre-listを元にシャッフル表示する機能を追加しています。 以下ように追加すれば、お好みのジャンルを毎分シャッフルして表示します。

;; シャッフル設定
(require 'bijin-tokei)
(setq bijin-tokei-genre-list (list "tokyo" "cc" "megane"))
(setq bijin-tokei-shuffle-genre t)
  • 起動方法
M-x bijin-tokei-start
または
M-x bijin-tokei-start-other-window
  • 終了方法
M-x bijin-tokei-stop
または
*bijin-tokei* のウインドウで'q'を押す

Cygwin(32bit)で野良ビルドしたEmacs上で使ったとき、突然SegfaultでEmacsごと お亡くなりになるという現象に遭遇しました。原因を特定するには至って いませんが、ワークアラウンド方法としてImageMagickのconvertコマンドを 直接実行せずにshellを介して別のコマンドを使って画像のリサイズを 行えば取りあえず大丈夫そうという事が判ったので、150329版から 画像リサイズのコマンド列をカスタマイズできるようにしました。 TANEの環境では以下の設定を.emacsに追加しています。

Cygwin(32bit) を2.3.0-1にアップデートしてから、Emacs+美人時計でSegfaultが 発生する事は無くなりました。恐らくCygwinかnewlibのバグだったのではないかと 推測します。もう使う事は無いかも知れませんが、ImageMagicが無い場合や、将来もっと良い リサイズコマンドが作られる事があるかも知れませんので、画像リサイズの カスタマイズ機構はそのまま残しておくことにします。

(setq bijin-tokei-image-convert-direct nil)
(setq bijin-tokei-image-sh "ash")
(setq bijin-tokei-image-conv-topng
      '("djpeg" "-ppm" "|" "pnmscale" "-xysize" (format "%s" (nth 1 args)) (format "%s" (nth 2 args)) "|" "pnmtopng"))
(setq bijin-tokei-image-conv-tojpg
      '("djpeg" "-ppm" "|" "pnmscale" "-xysize" (format "%s" (nth 1 args)) (format "%s" (nth 2 args)) "|" "cjpeg" "-quality" "100"))
(setq bijin-tokei-image-conv-togif
      '("djpeg" "-ppm" "|" "pnmscale" "-xysize" (format "%s" (nth 1 args)) (format "%s" (nth 2 args)) "|" "cjpeg" "-quality" "100" "|" "djpeg" "-gif"))

この設定では、IJG のJPEGツール(djpeg,cjpeg)と、 Netpbm のリサイズコマンド(pnmscale), PNG変換コマンド(pnmtopng)を使用しますので、別途用意しておく必要があります。 Cygwinであればいずれもパッケージインストール可能です。

4.2 emneko

[neko on Emacs]

歴史のある由緒正しい猫です (参考Wikipedia)。 原作はPC-9801シリーズ時代まで遡るようですが、 xnekoという名でUNIX系では有名な猫です。本当はカーソルを 追いかけるアクセサリなのですが、ELISP力が低くて動かすだけで 精一杯(^^;

emneko-20161216.tar.xz

  • .emacs設定例
(add-to-list 'load-path "/usr/share/emacs/site-lisp/emneko") ;emnekoディレクトリを/usr/share/emacs/site-lisp/に置いた場合
(require 'emneko)
  • 起動方法
M-x emneko-start
または
M-x emneko-start-other-window
  • 終了方法
M-x emneko-stop
または
*emneko* のウインドウで'q'を押す

4.3 dclock(DigitalClock)

[DigitalClock on Emacs]

訳あって少し大きめ表示の時計をEmacs上で動かしたい要件があったので作ってみました。

dclock-161216.tar.xz

  • .emacs設定例
(require 'dclock)
  • 起動方法
M-x dclock-start
または
M-x dclock-start-other-window
  • 終了方法
M-x dclock-stop
または
*DigitalClock* のウインドウで'q'を押す

少々煩雑ではありますが、表示フォーマットや部分的なフォント変更などに対応しています。 例えば.emacsに以下のような設定を行うと(「7セグ・14セグフォント 「DSEG」」 というフォントは別途インストール済みとします)

(require 'dclock)
(setq dclock-font-scale 9)
(setq dclock-font-faces '(:family "DSEG7 Classic" :foundry "outline" :slant italic :weight bold))
(setq dclock-format-time-string "%y年%m月%d日(%a)\n%H:%M %S")
(setq dclock-display-properties
  '(lambda () ""
     (progn
       (add-text-properties    (point-min)    (+ (point-min) 13) '(face (:family "Impact" :height 0.3 :weight normal)))
       (add-text-properties (- (point-max) 3)    (point-max)     '(face (:height 0.5)))
       )))

dclock_151107b.png

という感じの表示に変更できます。

4.4 sysmon(SystemMonitor)

[SystemMonitor on Emacs]

CPU負荷の情報を取得する方法を知ったので、Emacs上で表示できないかと作ってみました。

sysmon_161223.tar.xz

システムとして /proc/stat ファイルがサポートされている必要があります。 LinuxとCygwinで動作する事は確認しましたが、他のOSやSYSTEMで動くかは確認 していません。

  • .emacs設定例
(require 'sysmon)
;;以下必要に応じて設定
(if (window-system)
    (progn
      (setq sysmon-plotchar ?▌)
      (setq sysmon-disp-faceprop (list :foreground "blue" :height 1.5))
      (setq sysmon-plot-faceprop (list :foreground "dim gray" ))
      ))
  • 起動方法
M-x sysmon-start
または
M-x sysmon-start-other-window
  • 終了方法
M-x sysmon-stop
または
*System-Monitor* のウインドウで'q'を押す

161223版から表示のカスタマイズを行えるようにしてみました。

sysmon_161223_GUI.png

以下の変数を.emacsなどに記述する事でカスタマイズを行います。

変数 説明
sysmon-plotchar プロット文字を指定します。Unicodeの半角文字を利用するのが良いでしょう。
sysmon-disp-faceprop 1行目の値文字の表示プロパティを指定します。
sysmon-plot-faceprop プロット文字の表示プロパティを指定します。

Emacs25ではtext-scale-set等によるフォントサイズの変更に表示行桁は追従します。

Emacs24では 「default-font-width および default-font-height」という関数が無い為、 実行するとエラーします(すなわちtext-scale-set等によるフォントサイズの変更に表示行桁は追従できません)。 この為、

(setq sysmon-disable-fitwindow t)

を.emacsに追加する必要があります。

4.5 mcalendar(Mini-Calendar)

[Mini-Calendar on Emacs]

表示だけのカレンダーアプリです。Emacsに標準装備されているcalendarは超高機能なの ですが、表示が3ヵ月固定だったりウインドウサイズが動的に変更されるのが常時表示 するには少し都合が悪かったので自作してみました。

mcalendar_170303.tar.xz

他のアクセサリアプリと違って 操作可能となっています。設定やキーバインドは以下の通り。

  • .emacs設定例
(require 'mcalendar)
(setq mcalendar-display-width        2) ; 横に並べるカレンダーの数を指定します
(setq mcalendar-display-height       1) ; 縦に並べるカレンダーの数を指定します
(setq mcalendar-display-holidayname  t) ; 祝日名の表示/非表示を指定します
(setq mcalendar-monthvec  mcalendar-month-ja1)    ; 月を日本語表示します
(setq mcalendar-daynames  mcalendar-daynames-ja)  ; 曜日を日本語表示します
(setq mcalendar-capformat mcalendar-capformat-ja) ; 日本語表示向けキャプションフォーマットを指定します
  • 起動方法
M-x mcalendar
または
M-x mcalendar-other-window
  • キーバインド
    • 'n' : 次月へ
    • 'p' : 先月へ
    • '.' : 今日へ
    • 'h' : 祝日名表示のトグル切り替え
    • 'q' : 終了

5 アクセサリの初期起動例

ウインドウレイアウトを毎回良い感じに調節するのも面倒臭いので、 以下のような関数を定義して初期ウインドウ立ち上げを行っています。

(defun start-my-accessories ()
  "アクセサリ類を起動する"
  (interactive)
  (progn
    (let* ((rootw-w (window-width))
           (rootw-h (window-height))
           (win0 (get-buffer-window (current-buffer)))
           (win1 (split-window nil (- rootw-h 17)))
           (win2 (split-window win1 7))
           (win3 (split-window win2 30 t))
           (win4 (split-window win3 43 t))
           (win5 (split-window win4 27 t))
           )
      (select-window win1) (sysmon-start)
      (select-window win2) (bijin-tokei-start)
      (select-window win3) (display-time-world)
      (select-window win4) (mcalendar)
      (select-window win5) (emneko-start)
      (dolist (buffer (buffer-list))
        (with-current-buffer buffer
          (if (string= (buffer-name buffer) "*wclock*") (text-scale-set -1.0))
          (if (string= (buffer-name buffer) "*System-Monitor*") (text-scale-set -4.0))
          ))
      (text-scale-set 0)
      (select-window win0)
      (setq window-config (current-window-configuration))
    )))

emacs起動後、「M-x start-my-accessories」を実行すると、以下のような感じ になります。

[アクセサリスタートアップ例]

フレームの下半分が起動したアクセサリアプリになります。流石にこれだと作業面積が狭いので、 実使用ではもう少し縦長で使用しています。

いくつか要素はありますがポイントは以下のような所でしょうか。

  • こちらのマニュアル などを参照してsplit-windowで分割を頑張る。scratchバッファでテストをして 良い感じになったら.emacsに取り込むのが定石でしょうか。 split-windowの戻り値は新しく生成した ウインドウ(のID的なもの)なので、それを保持しておきます。この例では win0は一番最初のウインドウ、win1~5が分割したウインドウのそれぞれの枠 という感じになってます。
  • select-windowでアクティブウインドウを切り替えてアプリを起動する。 これを繰り返します。
  • 必要に応じてフォントサイズの調節を行ってます。buffer-listで得られた バッファ一覧を順に走査していき、「*wclock*」もしくは「*System-Monitor*」だった場合は text-scale-setにより小さいフォントで表示しています。
  • 最後にtext-scale-setを0に戻し、select-windowで一番最初のウインドウを選択して、 ついでにウインドウレイアウトをセーブしています(そんな訳で作ってみた の最後の方参照)。

アクセサリ表示に限らず、何かしら作業向けのペインが決まっているような場合 などにも応用ができるかも知れません。

本当は.emacs内に起動自体を書ければ良いのですが、.emacsを読み込んでいる最中は windowのサイズ等が確定していない為か、うまく表示できませんでした。 そんな訳で、emacsが立ち上がったら最初に手動で実行するコマンドになってます(^^;

6 終わりに

アクセサリは無くても困らないソフトウェアの一つなのですが、 画像が表示できるようになり、非同期処理ができるようになり、実現する為の環境(ELISP) が備わっていて、画面も広く使えるとあれば、あってもいいよね? と思う訳です。

他にも色々な方法でアクセサリウインドウ表示を維持する事はできると思います。 window-plus.elは色々ある方法の一つのとして お役に立てれば幸いです。

7 履歴

  2015/03/01 : 初版。
  2015/03/02 : bijin-tokei-150301.tar.xz→bijin-tokei-150302.tar.xzに更新。
  2015/03/05 : 誤記修正。微修正。
  2015/03/21 : bijin-tokei-150302.tar.xz→bijin-tokei-150320.tar.xzに更新。その他微改版。
  2015/03/29 : bijin-tokei-150320.tar.xz→bijin-tokei-150329.tar.xzに更新。その他微改版。
  2015/11/22 : window-plusをv002→v003に更新/新規関数説明追加。美人時計でのSegfault情報を更新。dclockを追加。
  2016/02/05 : bijin-tokei-150329.tar.xz→bijin-tokei-160201.tar.xzに更新。
  2016/02/12 : window-plusをv003→v004に更新。
  2016/02/21 : sysmonを追加。
  2016/02/28 : mcalendarを追加。
  2016/03/05 : window-plusをv004→v005に更新。mcalendarを160228→160305に更新。
  2016/10/08 : mcalendarを160305→161008に更新。
  2016/11/06 : window-plusをv005→v006に更新。「アクセサリの初期起動例」の項を追記。
  2016/11/19 : window-plusをv006→v007に更新。
  2016/12/11 : sysmonを160221→161211に更新。
  2016/12/16 : window-plusをv007→v008に更新。
               bijin-tokeiを160201→161216に更新。
               emnekoを20150215→20161216に更新。
               dclockを151107→161216に更新。
               sysmonを161211→161216に更新。
               mcalendarを161008→161216に更新。
               他、文書改善。
  2016/12/23 : sysmonを161216→161223に更新。
  2017/01/01 : sysmonにEmacs24での対応を追記。
  2017/02/23 : mcalendarを161216→170223に更新。
  2017/03/03 : mcalendarを170223→170303に更新。

TOP PREV

著者: TANE

Emacs 25.1.1 (Org mode 9.0.3)