Closed mikoto2000 closed 3 weeks ago
CSI リクエストの応答は、Vim ではなく端末の仕事なのでお使いの端末が返してる値だと思います。
@mattn コメントありがとうございます。
CSI リクエストの応答は、Vim ではなく端末の仕事なのでお使いの端末が返してる値だと思います。
だとすると、wsltty 上で直接件の python スクリプトを実行したときにも値は入るものなのではないでしょうか?
CSI リクエストは端末がサポートする端末種別によって問い合わせ結果が異なります。
https://www.xfree86.org/current/ctlseqs.html
ここにある VT-XXX という部分です。端末によっては設定により自分の端末がどの端末として動作するかのふるまいを変える事ができる物もあります。(putty や teraterm など) screen/tmux の様に環境変数で挙動を変える物もあります。
おそらく wsltty がその CSI リクエストに対応していない(もしくは設定が CSI 14 を返せる状態ではない)のだと思います。
あとはその CSI リクエストをどこに投げているのかもあります。 ssh 超しの端末内で動作するアプリケーションからは、ウィンドウサイズは知る全てないので、おそらく 0 を返すと思います。
コンソールアプリの対応状況や設定・計算条件の問題という事はなんとなくわかりましたが、混乱しています。
Vim のターミナルは、 wsltty に依存せずに値を返しているのでしょうか? 何から値を取得しているのでしょう?
コンソールアプリである vim.exe や、WSL 内の vim は、画面のサイズやフォントサイズなどは知る術がありません。 それらのサイズを知っているのは端末ソフトウェアだけです。 つまり WezTerm や Windows Terminal だけが知っています。 CSI リクエストは、端末内アプリケーションが標準出力に対してリクエストを投げます。すると端末ソフトウェアがそれをフックして標準出力に送り返してきます。そうやってコンソールアプリケーションはウィンドウサイズ等を知ることができる様になっています。ただし前述のとおり、端末種別によってはこの問い合わせ機能は持っていない事になっていますので、wsltty の有無で結果が異なるのであれば、それは wsltty が何かをサポートしてないのだと思います。
実行環境に誤解があるかもしれません。
wsltty 上で直接スクリプトを起動するとゼロが返却され、 wsltty 上でコンソール版 vim を起動し、 :term した中でスクリプトを起動すると数値が入るという状態です。
wsltty 無しで確認しました。 Windows Terminal から起動した WSL で直接スクリプトを動かすと 0 0 が、その中で起動した vim の terminal で実行すると値が取れました。 Windows Terminal の CTRL-+ でフォントサイズを変えながら実行するとちゃんとサイズが取れているので、これは間違いなく
terminal -> libvterm -> vim -> windows terminal
という経路で CSI リクエストが飛ばされ、その応答が python スクリプトに返っている事になります。おそらくですが、Windows Terminal そのものは幅を返せる機能があるにもかかわらず、Windows Terminal から直接起動されている端末ソフトウェア(conpty?)が、対応してないだけなんだと思います。 おそらく WSL から1回、tmux や screen を起動すれば取れたりするかも。
ビンゴですね。
ありがとうございます。
というのが今の理解です。 元々の疑問である以下の答えとしては、「Vimは関係ない」で間違いなさそうですので、 issue を閉じたいと思います。
こちら、Vim はどういう仕組みでこの値が入っているのでしょうか? この値を正確なピクセル数に修正できる可能性はあるでしょうか?
@mattn 検証までしていただきありがとうございました
不思議なことが起こっています... tmux 上で直接スクリプトを動かした場合と、 tmux 上で vim を起動して :term してスクリプトを動かした場合でサイズが異なります。 縦はともかく横は同じ値になると思っていたのですが...どんな理由が考えられるでしょうか?
動画にしなくてもよかったですね...
vim の terminal を通すと width pixels と height pixels の順が逆になってる気がします。
os_unix.c にちょっと怪しいコードがありますね。
int
mch_report_winsize(int fd, int rows, int cols)
{
int tty_fd;
int retval = -1;
tty_fd = get_tty_fd(fd);
if (tty_fd < 0)
return FAIL;
# if defined(TIOCSWINSZ)
struct winsize ws;
ws.ws_col = cols;
ws.ws_row = rows;
ws.ws_xpixel = cols * 5;
ws.ws_ypixel = rows * 10;
retval = ioctl(tty_fd, TIOCSWINSZ, &ws);
ch_log(NULL, "ioctl(TIOCSWINSZ) %s", retval == 0 ? "success" : "failed");
# elif defined(TIOCSSIZE)
struct ttysize ts;
ts.ts_cols = cols;
ts.ts_lines = rows;
retval = ioctl(tty_fd, TIOCSSIZE, &ts);
ch_log(NULL, "ioctl(TIOCSSIZE) %s", retval == 0 ? "success" : "failed");
# endif
if (tty_fd != fd)
close(tty_fd);
return retval == 0 ? OK : FAIL;
}
えーとこれは、 「Vim の :term はそれ自身が端末なので、サイズを返却する側であるが、同時にコンソールアプリケーションであるためフォントのサイズがわからない。そのため、固定値の 5 と 10 で計算してアプリに報告している」 という認識で合っているでしょうか?
横から失礼します。 TIOCSWINSZ(2const) によると、ws_xpixel、ws_ypixel は unused となっています。
@gcrtnst man ではそうなっているのですが、一部ターミナルエミュレーターは、そこにぴピクセル数を入れてくれる実装になっているようです。
対応端末であれば、「親プロセスの tty に対して TIOCSWINSZ を発行し、サイズを取得、そこからフォントサイズを計算。計算したフォントサイズで 5 と 10 を置き換える」で正確な値を返せそうですかね...?
GUI の方の処理の検討も必要ですが...
前述のロジックを追加すると、それっぽい値にはなりますね...
diff --git a/src/os_unix.c b/src/os_unix.c
index 0aa6f3ca1..db6c9e284 100644
--- a/src/os_unix.c
+++ b/src/os_unix.c
@@ -4349,6 +4349,92 @@ mch_get_shellsize(void)
}
#if defined(FEAT_TERMINAL) || defined(PROTO)
+
+#if defined(TIOCSWINSZ)
+ int
+calc_x_font_size() {
+#if defined(FEAT_GUI)
+ if (!gui.in_use) {
+#endif
+ pid_t ppid = getppid();
+
+ // /proc/[親プロセスID]/fd/0 のパスを作成
+ char tty_path[256];
+ snprintf(tty_path, sizeof(tty_path), "/proc/%d/fd/0", ppid);
+
+ // シンボリックリンクから TTY デバイスのパスを取得
+ char actual_tty[256];
+ ssize_t len = readlink(tty_path, actual_tty, sizeof(actual_tty) - 1);
+ if (len == -1) {
+ return 5;
+ }
+ actual_tty[len] = '\0'; // 文字列の終端を追加
+
+ // TTY デバイスファイルをオープン
+ int tty_fd = open(tty_path, O_RDWR);
+ if (tty_fd == -1) {
+ return 5;
+ }
+
+ // ウィンドウサイズを取得するための構造体
+ struct winsize ws;
+ if (ioctl(tty_fd, TIOCGWINSZ, &ws) == -1) {
+ close(tty_fd);
+ return 5;
+ }
+
+ close(tty_fd);
+
+ return ws.ws_xpixel / ws.ws_col;
+#if defined(FEAT_GUI)
+ } else {
+ return 5;
+ }
+#endif
+}
+
+ int
+calc_y_font_size() {
+#if defined(FEAT_GUI)
+ if (!gui.in_use) {
+#endif
+ pid_t ppid = getppid();
+
+ // /proc/[親プロセスID]/fd/0 のパスを作成
+ char tty_path[256];
+ snprintf(tty_path, sizeof(tty_path), "/proc/%d/fd/0", ppid);
+
+ // シンボリックリンクから TTY デバイスのパスを取得
+ char actual_tty[256];
+ ssize_t len = readlink(tty_path, actual_tty, sizeof(actual_tty) - 1);
+ if (len == -1) {
+ return 10;
+ }
+ actual_tty[len] = '\0'; // 文字列の終端を追加
+
+ // TTY デバイスファイルをオープン
+ int tty_fd = open(tty_path, O_RDWR);
+ if (tty_fd == -1) {
+ return 10;
+ }
+
+ // ウィンドウサイズを取得するための構造体
+ struct winsize ws;
+ if (ioctl(tty_fd, TIOCGWINSZ, &ws) == -1) {
+ return 10;
+ }
+
+ close(tty_fd);
+
+ return ws.ws_ypixel / ws.ws_row;
+#if defined(FEAT_GUI)
+ } else {
+ return 10;
+ }
+#endif
+}
+#endif
+
/*
* Report the windows size "rows" and "cols" to tty "fd".
*/
@@ -4367,8 +4453,8 @@ mch_report_winsize(int fd, int rows, int cols)
ws.ws_col = cols;
ws.ws_row = rows;
- ws.ws_xpixel = cols * 5;
- ws.ws_ypixel = rows * 10;
+ ws.ws_xpixel = cols * calc_x_font_size();
+ ws.ws_ypixel = rows * calc_y_font_size();
retval = ioctl(tty_fd, TIOCSWINSZ, &ws);
ch_log(NULL, "ioctl(TIOCSWINSZ) %s", retval == 0 ? "success" : "failed");
# elif defined(TIOCSSIZE)
@@ -4385,6 +4471,7 @@ mch_report_winsize(int fd, int rows, int cols)
}
#endif
+
/*
* Try to set the window size to Rows and Columns.
*/
commit: https://github.com/mikoto2000/vim/commit/638a1ce91d773fb5dd89af734fa0a875db9f83bd
ピクセル数まで必要ないケースでパフォーマンス問題が発生しそう...
いちおう xterm の terminal report にはサイズを返す CSI はあるので、TIOCSWINSZ でなく端末に問い合わせたらもしかしたら(端末によっては)サイズが得られるかもです。
あれ? python スクリプトが TIOCGWINSZ をした応答として Vim の TIOCSWINSZ が発火するのだと思っていたのですが、違いましたでしょうか?
printf "\x1b[14t"
をシェルで実行して貰えますか。
wsltty 上で直接実行
printf "\x1b[14t"
528;880t
wsltty 上の tmux で実行
printf "\x1b[14t"
736;1280t
wsltty 上の tmux 上の Vim の :term で実行
printf "\x1b[14t"
(何も表示されず)
となりました。
wslbridge2 がピクセルをゼロで設定して返却していそう。 https://github.com/Biswa96/wslbridge2/blob/5f444e639cc5ff6eff8945094ee68d0583adaafe/src/wslbridge2-backend.cpp#L301-L303
とすると、件の対応をしたとしても、 wslbridge2 上で直接 Vim を起動した場合、ゼロが返却されることになるのか...。なるほどなあ
非常に道のりが遠いことがわかりましたのでこの issue はクローズとさせていただきます。 (おおもとのやりたいことは、 Sixel 画像をウィンドウにフィットさせるためにピクセルレベルでサイズを取得する方法を探していたという感じでした)
@mattn
やっと TIOCSWINSZ
と「端末に問い合わせ」の違いが解った気がします。
ioctl
ではなく fprintf
でエスケープシーケンスを送信することで、 wsltty 上で直接 Vim を開いた場合でもピクセルレベルのサイズ取得ができることを確認しました。
ありがとうございました!
質問の内容
Vim のターミナル上で
ioctl
のtermios.TIOCGWINSZ
を実行し、ターミナルのサイズを取得するスクリプトを実行しました。 See: https://github.com/mikoto2000/sixelPreviewer/blob/master/termsize.pywsltty 上で直接実行すると、ピクセル単位の幅と高さがゼロになるのに対し、 Vim のターミナル上で実行すると、ピクセル単位の幅と高さに値が入っていました。
ただ、入っている数値が大分小さい値になっています。 (前述のVimのスクリーンショットの場合、w:880, h:220 が画面上のサイズでした。
こちら、Vim はどういう仕組みでこの値が入っているのでしょうか? この値を正確なピクセル数に修正できる可能性はあるでしょうか?
Vimのバージョン
9.1.697
OSの種類/ディストリ/バージョン
使用している or 関係していそうなプラグイン
なし
その他
以下環境でも同様の結果となりました。