Yearly Archives: 2022

2022-08-14

2022年夏の新番組

タイトル 開始 時刻 配信
スプリガン -     Netflix
BASTARD!!(バスタード)-暗黒の破壊神- -     Netflix
× 神クズ☆アイドル 07/02 12:00 dアニメ
彼女、お借りします 第2期 -      
iiiあいすくりん2 07/02 08:00 dアニメ
むさしの! 07/02 23:30 dアニメ
リコリス・リコイル 07/02 23:30 ABEMA
シュート!Goal to the Future 07/08 22:00 dアニメ
× てっぺんっ!!!!!!!!!!!!!!! 07/02 22:00 ABEMA
Engage Kiss 07/02 25:00 dアニメ
うたわれるもの 二人の白皇 -      
× 連盟空軍航空魔法音楽隊ルミナスウィッチーズ 07/03 23:00 dアニメ
RWBY 氷雪帝国 07/03 23:00 dアニメ
× 森のくまさん、冬眠中。 07/03 25:00 dアニメ
ユーレイデコ 07/03 23:30 dアニメ
ようこそ実力至上主義の教室へ 2nd Season -      
カードファイト!! ヴァンガード will+Dress (Season3) -      
転生賢者の異世界ライフ ~第二の職業を得て、世界最強になりました~ 07/04 24:00 ABEMA
邪神ちゃんドロップキックX(第3期) -      
オーバーロードⅣ(第4期) -      
× 東京ミュウミュウ にゅ~♡ 07/09 00:00 dアニメ
金装のヴェルメイユ 07/10 00:00 dアニメ
メイドインアビス -烈日の黄金郷-        
新テニスの王子様 U-17 WORLD CUP        
異世界迷宮でハーレムを 07/13 00:30 dアニメ
異世界おじさん 07/06 23:30 dアニメ
× 継母の連れ子が元カノだった -      
× 咲う アルスノトリア すんっ! 07/11 00:00 dアニメ
ちみも 07/09 00:00 dアニメ
組長娘と世話係 07/07 22:30 dアニメ
それでも歩は寄せてくる 07/07 02:28 Amazon
よふかしのうた 07/08 12:00 dアニメ
惑星のさみだれ 07/08 12:00 dアニメ
× プリマドール 07/09 01:00 dアニメ
シャドーハウス 2nd Season -      
× ブッチギレ! 07/09? 22:30 Amazon
メガトン級ムサシ シーズン1特別篇 -      
Extreme Hearts 07/10 02:00 dアニメ
Extreme Hearts SxSxS 07/10 02:00 dアニメ
黒の召喚士 07/09 22:00 dアニメ
Dr.STONE 龍水(TVスペシャル)        
ハナビちゃんは遅れがち 07/10 22:00 dアニメ
異世界薬局 07/10 23:00 dアニメ
× KJファイル 07/11 12:00 dアニメ
オリエント-淡路島激闘編- 07/15 00:00 dアニメ
× シャインポスト        
5億年ボタン~菅原そうたのショートショート~ 07/15 00:30 dアニメ
はたらく魔王さま!!(第2期)        
ラブライブ!スーパースター!! 第2期        
ダンジョンに出会いを求めるのは間違っているだろうかIV        
× 最近雇ったメイドが怪しい 07/26 02:30 dアニメ
BanG Dream! Morfonication        
うたの☆プリンスさまっ♪ マジLOVEスターリッシュツアーズ~旅の始まり~        
風都探偵(仮面ライダーW続編)       U-NEXT
夜は猫といっしょ 08/03   YouTube
賭ケグルイ双(ツイン) 08/04     Netflix
リラックマと遊園地(第2期)       Netflix
D4DJ Doubel Mix        
僕のヒーローアカデミア 【アニメ新作オリジナルエピソード×2話】        
ROLY POLY PEOPLES        
トニカクカワイイ ~制服~(新作エピソード)        
× 世界の終わりに柴犬と(※漫画動画)        
アイドルランドプリパラ        

ちみもの三姉妹の目の描き方が三人とも違うのが好き。

2022-05-04

Emacs Lispでline-prefixを使うと画像上のマウスイベントの座標がずれる件

再現コード:

(progn
  (require 'svg)
  (goto-char (point-min))
  ;; (insert "\n") ;;これがあると発生しづらい(スクロールして画像を一番上にもってくれば発生する)
  (let ((ov (make-overlay (point) (progn (insert " ") (point)) nil t)))
    (overlay-put ov 'evaporate t)
    (overlay-put ov 'pointer 'arrow)
    (overlay-put ov 'line-prefix "XXX") ;;これが無ければ発生しない(実際の状況ではこれが無くてもorg-indentがテキストプロパティのline-prefixを変更するといったことでこれと同じ事になる)
    (overlay-put ov 'display
                 (let ((svg (svg-create 400 300)))
                   (svg-rectangle svg 0 0 400 300 :fill "blue")
                   (svg-text svg "Click here"
                             :x 200 :y 150
                             :text-anchor "middle" :fill "white")
                   (svg-image svg)))
    (overlay-put ov 'keymap
                 (let ((km (make-sparse-keymap)))
                   (define-key km [mouse-1]
                     (lambda (ev)
                       (interactive "e")
                       (message "xy=%s" (posn-object-x-y (event-start ev)))))
                   km)))
  (insert "\n"))

上のコードをscratchバッファで実行すると画像が表示される。その画像をクリックするとミニバッファに座標が表示されるが、画像の左上からの座標になっていないことが分かる。原点は画像の左上では無く「XXX」の左上になっている。

画像の左端、line-prefix文字列の上端付近をクリックしたときの座標
図1: 画像の左端、line-prefix文字列の上端付近をクリックしたときの座標

この現象は画像がウィンドウの先頭にあり、その左にline-prefixによる文字列があるときのみ起こる。(line-prefixはテキストプロパティオーバーレイプロパティline-prefix変数のいずれで設定しても現象は起きる)

Emacs Lisp Reference Manualの22.7.4 Click Eventsには、マウスイベントのdx,dyはobjectの左上角を(0 . 0)とする相対座標と書かれている。今回の場合はobjectは画像('imageで始まるdisplayプロパティ)になるので、画像の左上からの座標になっていなければならない。しかし、画像がウィンドウの先頭にあって、かつ、line-prefixが使われている時に限り、line-prefix文字列の左上からの座標になってしまっている。画像がウィンドウの先頭に無いときや、line-prefixが使われていないときは正しく画像左上からの座標になっている。動作に一貫性が無く、文書化されている仕様と異なる結果が生じているのでEmacsの問題と言って良いのではないかと思う。

回避策としては、マウスの入力を必要とする時だけline-prefixを無効化するといったことが考えられる。

実際にel-easydrawにおいて作図エディタがウィンドウの一番上にあるときに座標がずれる問題が発生していた。org-indentを有効にすると発生する。org-indentはline-prefixやwrap-prefixを使用するので、まさに今回のケースに当てはまる。コミット701bfaaにて一時的にline-prefixを無効化することで回避した。

おそらくEmacsのバグだと思うが、この問題に気がつく人はどのくらいいるのだろうか。Emacs内に画像を表示してその中に対してマウス入力するなんてことを伊達や酔狂で無く本気でやっている人はどのくらいいるのだろうか。

2022-04-11

Windows上のEmacs 28.1でネイティブコンパイルする方法(まとめ)

(2025-02-24追記: Emacs 30.1での設定はこちら)

(2024-02-19追記: 手っ取り早く使い方を知りたい人はMS-Windows版 Emacs 29.1への移行作業をご覧下さい)

前回の続き。

前提

  • MSYS2のMinGW64環境下でGCCが使える状況になっていること。
  • MSYS2にlibgccjitパッケージが入っていること。
  • 公式で配布しているWindowsバイナリ emacs-28.1.zip を使用すること。

MSYS2のセットアップについては割愛します。Emacsはlibgccjitを使用してネイティブコンパイルを行うため、それに関連したファイル群をMSYS2から入手する必要があります。おそらく base-devel, mingw-w64-x86_64-toolchain, mingw-w64-x86_64-libgccjit あたりのパッケージが入っていれば良いのだと思います。

ただし実際にEmacsを使用するときにはMSYS2全体は必ずしも必要なく、少数のファイルだけコピーして他のPCに移すことも出来ます。

ここでは公式配布の emacs-28.1.zip を前提に説明しますが、自分で --with-native-compilation を指定してビルドしたEmacsを他の環境へ移す場合にも同じ考え方が適用出来ると思います。ちなみに emacs-28.1.zip は --with-native-compilation を指定してビルドされていますが(C-h v system-configuration-optionsで確認できる)ネイティブコンパイルに必要なDLL等が含まれていない状態です。

方法1:Emacs起動前からmingw64/binへPATHを通す

Emacsが起動する前に、環境変数PATHにMSYS2内の mingw64/bin ディレクトリが含まれていればネイティブコンパイルは正常に動作します(もちろんlibgccjitパッケージがインストールされていること)。

Windowsの「システムの詳細設定」で環境変数を変更するか、次のようなbatファイルを経由するといった方法が考えられます。

set PATH=c:/hogehoge/msys/mingw64/bin;%PATH%
c:/hogehoge/emacs-28.1/bin/runemacs.exe

mingw64/bin/には libgccjit-0.dll, as.exe, ld.exe が含まれている必要があります。また、 mingw64/lib/以下には必要なライブラリが含まれている必要があります。

ここで大事なのは「Emacsの起動前から」という点です。

環境変数を変更するには early-init.el や init.el 内でsetenv関数を呼び出す方法もありますが、それだと起動後すぐのネイティブコンパイルには適用されません。一番最初のネイティブコンパイルは early-init.el よりも前に起動する場合があります。なので early-init.el で (setenv "PATH" ~) しても手遅れな場合があります。

この方法は環境変数PATHを常時変更するため、人によっては許容できない場合があります。例えばCygwinやその他GNUツールを含むプロダクトに既にPATHが通っていてEmacs使用中にそのコマンドを使いたい場合です。

方法2:必要なファイルをEmacsにコピーして必要なときだけPATHを通す

alpha.gnu.org has shiny new Emacs 28.0.91 Windows binaries : emacs のコメントによれば、ネイティブコンパイルに必要なファイルは次の17個だそうです。

  • binに入れるもの
    • libgccjit-0.dll
    • (追記: 2022-09-23)libisl-23.dll (←最新のlibgccjitが必要とするdll)
    • (追記: 2023-07-31)Emacs29.1が出たので入れ直しましたが、libmpc-3.dllとlibmpfr-6.dllも増えていました(MS-Windows版 Emacs 29.1へ移行)
  • lib/gccに入れるもの
    • crtbegin.o
    • crtend.o
    • dllcrt2.o
    • libadvapi32.a
    • libgcc.a
    • libgcc_s.a
    • libkernel32.a
    • libmingw32.a
    • libmingwex.a
    • libmoldname.a
    • libmsvcrt.a
    • libpthread.a
    • libshell32.a
    • libuser32.a
    • ld.exe
    • as.exe (ldとasはリンク先では libexec/emacs/28.0.91/x86_64-w64-mingw32/ とありますが、libexec/emacs/28.1/x86_64-w64-mingw32に置いても認識されず、色々試したところlib/gccに置いたら認識されました)

これらをemacs-28.1.zipを展開して出来たディレクトリの適切なディレクトリへコピーするとネイティブコンパイルが条件付きで動作するようになります。libgccjit-0.dll は bin (既にrunemacs.exe等があるディレクトリ)へ、それ以外は lib (既にemacs/やsystemd/がある)の下にgccというディレクトリを作成してその中へ入れてください。

これだけである程度の割合でネイティブコンパイルが成功するようになるのですが、自動で起動する非同期コンパイルがなぜか失敗する場合があります(カレントディレクトリがbinやlib等の階層に無い場合にas.exeが無いと言われます)。

そこで early-init.el (あるいはinit.el)に次のコードを追加します。

(2022-09-23追記: Windows版Emacsを28.1に上げたのでNative Compilationフィーバーに便乗する - Qiitaにあるように native-comp-driver-options 変数に -B オプションを指定した方が良さそうです。私はearly-init.elで (setq native-comp-driver-options (list "-B" (expand-file-name (file-name-concat invocation-directory "../lib/gcc")) )) のようにしました)

(when (and (fboundp #'native-comp-available-p) ;;emacs-28以降
           (native-comp-available-p) ;;libgccjitが使える
           (eq system-type 'windows-nt)) ;;Windowsの場合 (他必要に応じて条件を追加すること)
  (defun my-comp-wrap-process-call (orig-fun &rest args)
    (let* (;; emacs.exeのあるディレクトリの一つ上のlib/gcc
           (lib-dir (expand-file-name
                     (file-name-concat invocation-directory "../lib/gcc")))
           ;; 環境変数PATHとLIBRARY_PATHを一時的に変更
           (process-environment
            (append
             (list (concat "PATH=" lib-dir ";" (getenv "PATH"))
                   (concat "LIBRARY_PATH=" lib-dir))
             process-environment)))
      ;; 元の関数を呼び出す
      (apply orig-fun args)))

  ;; コンパイル用にemacsを起動する関数をラップする
  (advice-add #'comp-final :around #'my-comp-wrap-process-call)
  (advice-add #'comp-run-async-workers :around #'my-comp-wrap-process-call))

このコードはネイティブコンパイルのために別プロセスでemacsを起動するときにだけ環境変数PATHとLIBRARY_PATHを変更します。起動されたemacsは最初からネイティブコンパイルに必要なコマンドにPATHが通った状態になります。

つまり、early-init.elより前に起動するネイティブコンパイルについてはlib/gccに置いたas.exeやld.exeが使われることによって解決し、それ以降なぜかエラーになるケースについては環境変数の一時的な変更で解決します。

ちなみに前者はあらかじめ必要なファイルをネイティブコンパイルしておくことによって回避可能です。エラーが出るファイルを手動でネイティブコンパイルしたり、試してはいませんが NATIVE_FULL_AOT=1 でビルドすると回避できるかもしれません。

なお、既にCygwin等にPATHが通っていると、設定が間違っていてもCygwinのas.exeやld.exeが起動する場合があるので注意が必要です。

なぜか失敗するケースは、調べた限りコンパイル時のカレントディレクトリによるようなので次のようなコードでも良いかもしれません。ただ、ネイティブコンパイルがカレントディレクトリに依存している(または将来するようになる)とまずいかもしれません。

(when (and (fboundp #'native-comp-available-p) ;;emacs-28以降
           (native-comp-available-p) ;;libgccjitが使える
           (eq system-type 'windows-nt)) ;;Windowsの場合 (他必要に応じて条件を追加すること)
  (defun my-comp-wrap-process-call (orig-fun &rest args)
    ;; 一時的にカレントディレクトリを emacs-28.1/bin にする
    ;; でないと emacs-28.1/lib/gcc/as.exe を見つけてくれない
    (let ((default-directory invocation-directory))
      ;; 元の関数を呼び出す
      (apply orig-fun args)))

  ;; コンパイル用にemacsを起動する関数をラップする
  (advice-add #'comp-final :around #'my-comp-wrap-process-call)
  (advice-add #'comp-run-async-workers :around #'my-comp-wrap-process-call))

方法3:comp.elを修正する

最も単純な方法は comp.el を修正することだと思います。

前述の通りネイティブコンパイルはearly-init.elよりも前に起動する場合があり、comp.elもその時にロードされます。

従ってearly-init.elやinit.elで挙動を完全に修正するのは無理なので、comp.elを直接書き替えてしまった方が素直な方法となるでしょう。(Emacsの初期化プロセスについて詳しくないので他に何か方法があったらすみません)

具体的には、前と同じようなことをcomp.elの末尾に書き加えてやれば良いでしょう。例えば:

;; share/emacs/28.1/lisp/emacs-lisp/comp.el の末尾、(provide 'comp)の前に以下を追加
(defconst my-comp-tool-path "c:/hogehoge/msys/mingw64/bin") ;;自分のmingw64/binの場所

(defun my-comp-wrap-process-call (orig-fun &rest args)
  (let* ((process-environment
          (cons
           (concat "PATH=" my-comp-tool-path ";" (getenv "PATH"))
           process-environment)))
    ;; 元の関数を呼び出す
    (apply orig-fun args)))

;; コンパイル用にemacsを起動する関数をラップする
(advice-add #'comp-final :around #'my-comp-wrap-process-call)
(advice-add #'comp-run-async-workers :around #'my-comp-wrap-process-call)

my-comp-tool-pathにはmingw64/binの場所を指定してください。もしくは前にやったようにemacs.exeの位置から相対的に割り出しても良いでしょう(関連ファイルのコピーが必要になりますが)。

ファイル(comp.el)の途中を書き替えるのも分かりづらいかなと思ったのでadviceのままにしてあります。make-processやcall-processの前後を直接書き替えても良いでしょう。

comp.elを書き替えたら対応する.elcや.elnを削除するのもお忘れ無く。

(追記:2022-09-23)Emacs 28.2でNative Compileする

Emacs 28.2がリリースされてWindows版の公式ビルドも公開されました。

Index of /gnu/emacs/windows/emacs-28

emacs-28.2.zipを展開して、MSYS2のlibgccjit-0.dllとlibisl-23.dllをemacs-28.2/binへ、その他のライブラリやらas.exe、ld.exeやらをemacs-28.2/lib/gcc/へコピーし、early-init.elで (setq native-comp-driver-options (list "-B" (expand-file-name (file-name-concat invocation-directory "../lib/gcc")) )) と指定しただけで問題なくNative Compileできるようになりました。

ちなみに、最初MSYS2最新のlibgccjit-0.dllをbinに入れてもNative Compileが有効になりませんでした((native-comp-available-p)がnil)。

それをTwitterに書いたところ、libisl-23.dllも必要になったとの情報を頂きました。

調べてみると確かに最新のlibgccjit-0.dll内にはlibisl-23.dllという文字列があります。libgccjitのバージョンアップに伴い依存するdllが増えたようです。libisl-23.dllもemacs-28.2/binへコピーしたところnative-comp-available-pがtになりました。

こういうことがあるからバイナリ配布するならlibgccjit(や関連ファイル)も一緒に配布してほしいものです。

(追記:2023-07-31)Emacs 29.1

MS-Windows版 Emacs 29.1へ移行に書きました。dllが二つ増えていただけです。