画面サイズの取得
画面サイズの取得には、libcのioctlを利用し、TIOCGWINSZをシステムコールする。
Cargo.tomlにはlibcを追記する。
[dependencies] libc = "0.2"
ioctlにはファイルディスクリプタを渡す必要があり、libc::STDOUT_FILENOが使えるが、アプリケーション側にSIGWINCHが上手く送られない場合(CLIツール等を作成している場合)に、Terminalを制御するウィンドウのサイズ変更に動的に対応できない。そのため"/dev/tty"を読む方法も用意する。
use libc::{ioctl, winsize, TIOCGWINSZ, STDOUT_FILENO}; use std::{mem, fs::File, os::unix::io::IntoRawFd}; use std::{thread, time}; pub fn terminal_size() -> Option<winsize> { // STDOUT_FILENOか/dev/ttyを利用する let fd = if let Ok(file) = File::open("/dev/tty"){ file.into_raw_fd() }else { STDOUT_FILENO }; // ファイルディスクリプタに対してTIOCGWINSZをシステムコール let mut ws: winsize = unsafe { mem::zeroed() }; if unsafe { ioctl(fd, TIOCGWINSZ, &mut ws) } == -1 { None } else { Some(ws) } } // 1秒毎に画面サイズを表示 fn main(){ loop{ match terminal_size(){ Some(ws) => println!("h:{},w:{}", ws.ws_row, ws.ws_col), None => println!("failure") } thread::sleep(time::Duration::from_millis(1000)); } }
実際の実行例は以下のようになる。
調査ログ
2020/03/23に調査したログ。
terminalサイズの取得を行うライブラリを調べると以下が出てくる。
- GitHub - clap-rs/term_size-rs: Functions for determining terminal sizes in Rust
- GitHub - eminence/terminal-size: Rust library to getting the size of your terminal
- GitHub - softprops/termsize: terminal size matters
大体実装は同じだが、全てSTDOUT_FILENOを使っている。
クロスプラットフォーム観点でWindows対応の参考になる。
rustのターミナルだと以下が出てくる
- GitHub - cpjreynolds/rustty: A terminal UI library
- GitHub - murarth/mortal: Concurrent cross-platform terminal interface
NixSignalやlibcを使って画面サイズが変化したSignal(SIGWINCH)をキャッチして画面サイズ変更に対応している。そういう方法もあるか。
最終的に参考にしたのは以下のクロスプラットフォームターミナルの実装。
github.com
以下のブログやissueも読んだ。
hermanradtke.com
github.com