Hatena::ブログ(Diary)

Okiraku Programming RSSフィード Twitter

2016-09-23 keyhacでNICOLA配列

keyhacでNICOLA配列を実現する

macOS Sierraにアップデートしたところ、キー配列をカスタマイズするKarabinerが利用できなくなってしまいました。


Karabinerは非常に様々なカスタマイズに対応していたため、不便な思いをしている人も多そう。

私はNICOLA配列(いわゆる親指シフト入力)で日本語を入力するのにKarabinerを使っていたのですが、これができなくなっていました。

親指シフトは同時押し(シフトキーを押しながら文字キーを押すだけでなく、文字キーを押してからシフトキーを押し下げても良い)が特徴のため、単純なキーの置き換えだけができるツールでは再現ができません。

そんな折、以下のページでkeyhacという、Pythonで高度にキー配列をカスタマイズできるツールを知りました。


macOS SierraでKarabinerが動かなくて困っている人に贈る代替ソフトウェア


Keyhac - Pythonによる柔軟なキーカスタマイズツール - craftware


これならというわけで、JIS配列キーボードローマ字設定の環境向けにNICOLA配列を実現する設定を書いてみました。設定といっても普通にPythonプログラムです。この記事も、これでNICOLA配列にした状態で書いています。

設定の仕方

keyhacを起動し、メニューバーに表示されたアイコンメニューから"設定の編集"を選ぶと、TextEditで設定ファイルが開きます。そこに、本記事末尾のコードコピペして上書きします。デフォルトの設定はいろんな機能のサンプルが入っていて意図しない動作になったりするので消すか無効化したほうがいいでしょう。

なお、TextEditは勝手にクォート("")を“”などと置き換えてしまうので、編集自動置換→スマート引用符の設定は切ってから編集したほうがいいでしょう。

シフトキーの設定は SHIFT_KEY = ["左親指シフトキー", "右親指シフトキー"] のように書きます。キーの名称は、keyhacのメニューから"端末を表示"し、同じくメニューから"内部ログ ON"にしてタイプしてみれば確認できます。

設定を書いたら保存して、再度メニューから"設定のリロード"を選べば準備完了です。


使い方

かなキーを押すとNICOLA配列有効化され、英数キーを押すと無効化されます。

親指シフトキーは同時押しが可能です。具体的には、親指シフトキーの押下前後40ms以内に文字キーが押されると一回だけ効くようになっています。文字キーの後でシフトキーを押下した場合に対応するため、文字キー単体で入力された場合は最大40msだけキーダウンが遅延されます(キーアップするか親指シフトキーが押されれば直ちに入力されます)。この判定時間はSHIFT_MSとSHIFT_DURATIONを両方とも変更することで、個々人の好みに合わせて調整が可能です。

一応これでそれなりに機能しているようですが、文字キー → 親指シフトキー → 文字キー と高速打鍵すると意図しない方の文字シフトが効いたりすることも稀にあるため、まだ改善余地があるかもしれません*1


さらなるカスタマイズ…

キー配列TABLE という辞書編集することでカスタマイズが可能です。各文字に対して/(スラッシュ)区切りで'シフトなし/左親指シフト/右親指シフト'という順にローマ字表記、または""でくくったキー名を書くようになっています。頑張ればorz配列等にも対応できるはずです。

この設定は下記のNICOLA-J配列を参考にしてますが、"[", "]", "^", "\" キーはそのままにしてあります。

http://nicola.sunicom.co.jp/image/nicola-j-implementation-sample.png


なおドキュメントはメニューのヘルプで見られますが、delayedCall などの一部の低レベルの機能は載っていないので、そういう細かい部分はkeyhacやその下位ライブラリのpyauto, ctkitのソースを見る必要があるかもしれません。

GitHub - crftwr/keyhac: python based key customization utility

GitHub - crftwr/pyauto: windows low-level feature library

GitHub - crftwr/ckit: craftware's base library


設定ファイル

https://gist.github.com/NeoCat/d7233e3130110c0fe6122d32610e4d50

*1文字キーを離す前に親指シフトキーを押した場合、どう判定するべきかが難しいところ。近い方の文字キーにすべきなのかもしれませんが、これを実現するには入力をタイマーで遅延させる必要があり、かなり複雑なことになりそう……。

2016-08-20 USB直結のスクリーンセーバーブロッカー

2016-06-23 マイナンバーカードでSSHしてみた

マイナンバーカードでSSHしてみた

マイナンバーカードでSSHする - AAA Blog


という記事が出ていたので、マイナンバーカードを使ったSSHログインLinux上でやってみました。環境は出たばっかりのFedora 24です。

カードリーダーは、上記の記事のような非接触式ではなく、接触式のICカードリーダー NTTCom SCR3310を利用しました。



まずは必要なパッケージをインストールカードリーダーUSB接続し、スマートカードデーモンpcscdを起動します。

$ sudo dnf install openssl-devel readline-devel zlib-devel pcsc-lite pcsc-lite-devel pcsc-lite-ccid pcsc-tools
$ sudo systemctl start pcscd

開発中の個人番号カード対応版OpenSCをcloneしてきて、ビルドインストールします。

$ git clone https://github.com/jpki/OpenSC.git
$ cd OpenSC
$ ./bootstrap
$ ./configure
$ make
$ sudo make install

ではカードをセットして、公開鍵を読み出しましょう。カードの裏表を間違えないように注意(ICチップの端子のある面が上)。間違ってると読めないというエラーになります。。

$ pkcs15-tool --read-ssh-key 1
Using reader with a card: NTT Communications Corp. SCR3310-NTTCom USB SmartCard Reader [Vendor Interface] 00 00
ssh-rsa *******...... User Authentication Certificate
zsh: bus error  sudo =pkcs15-tool --read-ssh-key 1

ってなんかバスエラーを起こしました。がとりあえず読めたから気にしない(何


これをコピペしてSSHサーバの ~/.ssh/authorized_keys に追加します。同一マシンローカルホスト)でもいいでしょう。


そうしておいて、ビルドしたOpenSCのPKCSモジュール指定してSSHログインしてみると...

$ ssh 192.168.xx.yy -I /usr/local/lib/opensc-pkcs11.so
Enter PIN for 'JPKI (User Authentication PIN)': **** ← 公的個人認証用の4桁のPIN番号
Last login: Fri Jun 24 00:37:23 2016 from 192.168.xx.zz
$ 

無事ログインできました。


なおPIN番号は3回間違えると閉塞されてしまい役所手続きが必要になるのでご注意を。。

(3種類ある4桁の暗証番号のうちの、利用者証明電子証明書の(マイナポータルへのログインコンビニでの公的証明書の交付などで使う)PIN番号です。)


他に

$ pkcs15-tool -c 

認証証明書シリアル番号などを確認したり、

$ pkcs15-tool -r 1 

認証証明書情報を読み出し、ファイルコピペして(リダイレクトするとSEGVするせいで書き出されない…)

$ openssl x509 -text -noout -in <ファイル名>

などとすると有効期限や発行者情報自治体とか)が確認できます。認証証明書の方にはマイナンバーはもちろん、住所や氏名などは入っていないことが確認できます。

2016-05-21 C++11/14でrange based forで指定回数Loopする

C++11/14でrange based forで指定回数Loopする

for (int i = 0; i < 10; i++) { ... }

って何度も書くのがだるいので、range based forを使って楽にしたい。要するに、

for (auto i : 10_) { ... }

で10回ループできるようにしたいね、という話。他の変数の回数分ループするなら

int x = 3;
for (auto i : Loop(x)) { ... }

などとする。


なお、ループ変数を途中で書き換えたいという不届きな向きには

for (auto& i : 10_) { ...; i += 2; }

などとすれば良い。auto&&でも良い。


なおあまり複雑なオブジェクトをrangeに渡してしまうと最適化が効きにくくなるという弊害が出てしまうので、最初の表現とほぼ等価に展開されるようなものを渡したい。どうやって実現するかというと、range based for

for ( range_declaration : range_expression ) loop_statement

において、range_expression が begin(), end()メソッドを持つ場合は

{
  auto && __range = range_expression;
  for (auto __begin = range_expression.begin(), __end = range_expression.end(); __begin != __end; ++__begin) {
    range_declaration = *__begin;
    loop_statement
  }
}

等価ということになっている。まずカウンタが取れるように、operator*がループ変数の参照を返すようにすれば良いだろう。終了判定は __begin != __end の部分なので、operator!=を適当に書き換えてやればよさそうである。あとoperator++(&)でインクリメント。


というわけでこんな感じになった。Apple clang-703.0.31(-std=c++14)とg++5.1(-std=c++14)で確認。

clangの方で確認した限り、一応STLを使ったりしても、-O2あたりの結果をディスアセンブルしてみると普通にfor loopを書いた時と同等の最適化がされるようである(もっと複雑な場合だと変わってくるのかもしれないが未確認)。


http://ideone.com/n9zYGV

template <typename T>
class LoopTemplate {
	T max, cnt;
public:
	LoopTemplate(T max) : max(max), cnt(0) {}
	LoopTemplate(T min, T max) : max(max), cnt(min) {}
	LoopTemplate& begin() {
		return *this;
	}
	LoopTemplate& operator++() {
		cnt++;
		return *this;
	}
	T& operator*() {
		return cnt;
	}
	bool operator!=(LoopTemplate&) {
		return cnt < max;
	}
	LoopTemplate& end() {
		return *this;
	}
};
typedef LoopTemplate<long> Loop;
LoopTemplate<unsigned long long> operator "" _(unsigned long long n)
{
	return LoopTemplate<unsigned long long>(n);
}


/* Exapmle usage */
#include <iostream>

void f(unsigned long long i)
{
	std::cout << i << std::endl;
}

int main()
{
	for (auto i : 10_) {
		f(i);
	}

	int x  = 3;
	for (auto&& i : Loop(x)) {
		f(i);
	}

	return 0;
}

2016-05-08 zshでネットワーク経由のファイル転送

zshでネットワーク経由のファイル転送

zsh(とtar)を使ってネットワーク経由のファイル転送を効率的に行う方法についてです。

ARMボード(RaspberryPiとかPCduino等)を使っていると、scpなどでホスト間で大きなファイル転送をする際に、CPUネックになりがち。rsyncやscpするときに暗号をarcfour等にする*1ことでCPU使用率を低減する方法もありますが、そもそもLAN内等で暗号化しなくても良い場合もあります。こういうとき、単一ファイルであれば、

受信側(host1): nc -l 12345 >file
送信側(host2): nc host1 12345 <file

ncを使えばOK。(ポートファイアウォールで塞がれていないことが前提ですが。)


複数ファイルだと

受信側(host1): nc -l 12345 | tar vxf -
送信側(host2): tar vcf - files/* | nc host1 12345

のように転送する方法がありますが、これだとパイプ経由でのデータコピーのためにncによってCPU使用率が上がってしまい、単発の場合と比べて性能が悪くなってしまいます。


bashであれば、送信側についてはncを経由せずに、

tar vcf - files >/dev/tcp/host1/12345

とすることで直接TCPソケットに書き出せます。が、受信側は知る限りこういった方法がありません。


zshであれば、標準でついてくるnet/tcpモジュールを使って、送受信側とも直接TCPソケットに読み書きさせることができます。

ただ、net/tcp提供する各コマンドシェルスクリプトTCPサーバを書くことを念頭に作られているようで、今回の目的にはローレベルのztcpコマンドを操作しないといけないようです。手動では若干面倒なので、.zshrc内にあらかじめ以下の関数を作っておきます。

function file_recv {
        if [ $# = 0 ]; then
                echo file_recv listen_port
                return
        fi
        (
        autoload -U tcp_open; tcp_open >/dev/null 2>/dev/null # define ztcp
        ztcp -l "$1"
        fd_listen=$REPLY
        echo "Waiting on port $1 (fd $fd_listen) ..."
        ztcp -a $fd_listen || echo failed.
        fd_accept=$REPLY
        echo "Connected. (fd $fd_accept)"
        tar vxf - <&$fd_accept
        ztcp -c $fd_listen
        ztcp -c $fd_accept
        )
}
function file_send {
        if [ $# -lt 3 ]; then
                echo file_send host port files ...
                return
        fi
        (
        autoload -U tcp_open; tcp_open >/dev/null 2>/dev/null # define ztcp
        ztcp "$1" "$2"
        shift 2
        tar vcf - "$@" >&$REPLY
        ztcp -c $REPLY
        )
}

あとは、

受信側(host1): file_recv 12345
送信側(host2): file_send host1 12345 files/*

とすればOKです。送信側は上記bashを使ってもOKです、その方が事前に準備要らないですからね。*2


しかしなんというか、ここまでするなら、tarも使わずsendfile(2)とかsplice(2)等を使ってユーザ空間でのコピーも避けられるようなさらに効率的な転送コマンドを用意する方がいい気がしてきた。


あと、tar vcf - ... の部分に任意のコマンドを指定できるようにした方が利用シーンが広がって有用かもしれない(効率のいいncとして使う)。

*1:参考: http://d.hatena.ne.jp/rx7/20101025/p1

*2:どちらから接続するかを入れ替えれば、"受信側は上記関数の準備不要"とするということもできるはず。