読者です 読者をやめる 読者になる 読者になる

ぐるっとぐりっど

日曜プログラマがいろいろ試してみたことを、後の自分のためにまとめておく場所

Emacs25に導入されたXWidgetに関して理解を深める

emacs

この記事は、Emacs Advent Calendar 2016の3日目の記事です。

はじめに

emacs25.1が2016年9月17日にリリースされました。
なかでも注目を(個人的に)浴びていたのが、もともとブランチして開発されていたものがマージされたXWidgetsです。

XWidgetsは、リリースノートを見てみると、

Xwidgets: a new feature for embedding native widgets inside Emacs buffers.

https://www.gnu.org/software/emacs/news/NEWS.25.1

と、ネイティブなウィジェットemacsのバッファに埋め込めるらしいです。
実際なにができるか用途がぱっとは思い浮ばないものの、可能性としてはおもしろいと思います。

EmacsWikiの記事を見てみると、emacsの中にemacsを埋め込むという、よくわからない状況になっています。

EmacsWiki: Emacs X Widgets


ただ、実際のところ、巷のブログ等見てみても、xwidget-webkit-browse-urlコマンドを使ってみた、程度の記事しかみつからず、ウィジェットの挿入方法がよくわかなかったので、ウィジェットの挿入方法を調べてみました。

(結論から書いておくと、emacs 25.1では、任意のウィジェットを挿入することはできません。M-x xwidget-webkit-browse-urlでネイティブブラウザをバッファに表示するぐらいしかできません)

ビルド

Xwidgetsはどのemacsでも利用できるわけではなく、ビルド時に、--with-xwidgetをつけた場合のみ利用することができます。
Xwidgetsが使えるかどうかは、M-x xwidget-webkit-browse-urlを実行してみるのがてっとりばやいと思います。有効でない場合は、

Your Emacs was not compiled with xwidgets support

と優しく教えてくれます。なお、xwidget-webkit-browse-urlの使い方は巷にあふれているので、そちらに譲ることにして、ここでは割愛します。

--with-xwidgetオプションを利用できるのは、今のところLinux(Unix)、Mac OSWindowsCygwin上のみのようです。Bash On Ubuntu On Windows上やmsys上でできないか調べたり、いろいろ試したりしてみたのですが、X Serverが無いので駄目のようです。残念。

サンプルコード

任意のWidgetとして、ボタンを挿入する超簡単なサンプルコードです。

(require 'xwidget)
(xwidget-insert (point-min) 'Button "Click me" 100 100)

これを評価すれば、

f:id:grugrut:20161129143317p:plain

な感じでボタンが表示されます。ボタンだったら、M-x customizeでも表示されるじゃんと思ってそちらのボタンも見てみたのですが、ちょっと見た目が違いますね。

f:id:grugrut:20161129143534p:plain

もちろんボタンだけでなく、スライダーや他のGtkWidgetを挿入することもできます。

(xwidget-insert (point) 'slider "slide me" 300 50)

(xwgir-require-namespace "Gtk" "3.0")
(put 'ColorButton1 :xwgir-class '("Gtk" "ColorSelection"))
(xwidget-insert (point) 'ColorButton1 "xwgir-color-button " 500 300)

f:id:grugrut:20161203122417p:plain

(put 'ColorButton1 :xwgir-class '("Gtk" "ColorSelection"))

で、デフォルトでは設定されていないwidgetを設定できるようなのですが、
https://developer.gnome.org/gtk3/stable/GtkWidget.html
配下にあるクラスをセットしても、表示されるものと表示されないものがあるので、なにかしら表示できるwidgetに条件があるようなのですが、そこは残念ながらわかっていません。

苦しんだところ

実はこのサンプルコード、emacs 25.1では動作しません。
評価するとセグフォでコアを吐いてemacsが死んでしまいます。ビルドミスかと思って調べたら、emacs 25.1の方には制限をかけてあってxwidget-webkit-browse-url以外の用途では使えないようになっているようです。

Xwidgetブランチのコードにあるsrc/xwidget.c
http://bzr.savannah.gnu.org/lh/emacs/xwidget/files

DEFUN ("make-xwidget", Fmake_xwidget, Smake_xwidget, 7, 8, 0,
         doc: /* Make an xwidget from BEG to END of TYPE. 

If BUFFER is nil it uses the current buffer. If BUFFER is a string and
no such buffer exists, it is created.

TYPE is a symbol which can take one of the following values:
- Button
- ToggleButton
- slider
- socket
- socket-osr
- cairo
*/

(中略)

  if (EQ(xw->type, Qwebkit_osr)||
      EQ(xw->type, Qsocket_osr)||
      (!NILP (Fget(xw->type, QCxwgir_class)))) {
      block_input();
      xw->widgetwindow_osr = gtk_offscreen_window_new ();

(後略)

emacs 25.1のコードにあるsrc/xwidget.c
https://github.com/emacs-mirror/emacs/tree/emacs-25

DEFUN ("make-xwidget",
       Fmake_xwidget, Smake_xwidget,
       5, 6, 0,
       doc: /* Make an xwidget of TYPE.
If BUFFER is nil, use the current buffer.
If BUFFER is a string and no such buffer exists, create it.
TYPE is a symbol which can take one of the following values:

- webkit

(中略)

  if (EQ (xw->type, Qwebkit))
    {
      block_input ();
      xw->widgetwindow_osr = gtk_offscreen_window_new ();

(後略)

記事としてまとめるために、もう一度ソース読んでたら、冒頭のコメントにばっちり書いてあったorz

ひさしぶりにCのソースを読んだせいで、いきなりコードの方を読むという冒険をしてしまったのが敗因です。

まとめ

  • emacs 25.1でXwidgetがマージされて、ネイティブウィジェットがバッファに表示されるようになった
  • マージはされたが、同じものがマージされたわけではないのでできることはかなり違う
  • ウィジェットを操作(ボタンならクリック、スライダーなら移動)したときに着火されるイベントがわからないと単に表示しておしまいなので、今後はその辺を調べたい。
    • 本当は、鬼軍曹.elGUIで、このキーを実行しろ、と表示するようなelispを書いてみたかった

明日4日目はinoueyuworksさんの「tabulated list 的な UI を量産するために」の予定です。楽しみですね。

補足

emacs25.1ではemacs inside emacsが実現できないかというとそうではなくて、jslinuxを開けばいいよ、とやさしい人がredditで回答してました。(なんかredditへのリンクを貼ると記事を保存できないので単なるテキストだけ残しておきます)

www.reddit.com/r/emacs/comments/4srze9/watching_youtube_inside_emacs_25/d5bvl4f/