PuTTYのウィンドウ位置を保存するようにしてみた

蛭子屋氏のPuTTY 0.60 ごった煮版2007年8月6日版(putty-0.60-JP_Y)に「起動時にウィンドウ位置が固定になる不具合の修正」と「ウィンドウ位置ほ保存する」カスタマイズを加えてみた。


せっかくなのでバイナリとパッチを公開してみるよ!
http://eureka.pasela.org/products/putty/


不具合修正しますた! 落とし直してくだちい。
不具合発覚

通信切断後に自動的にウィンドウが閉じる場合はウィンドウ位置が保存されない不具合が発覚><
どうもWM_CLOSEとかWM_DESTROYが飛んでこないっぽい。調べてみたらいきなりPostQuitMessage()してた。


というわけで、面倒ですが一旦「閉じる」ボタンとかAlt+F4なんかで終了させて位置を覚えさせてやってくだしあ><
会社から帰ったら頑張って対応してみる。

きっかけ

256カラーだったりplinkだったり色々と便利さがあって、少し前にTeraTermから乗り換えたんだが、ひとつだけ不満点がった。
それはウィンドウ位置が何故かY座標の0……つまり画面の一番上で必ず起動するということ(X座標は若干ランダムだった)。
タスクバーを上に配置していると、タスクバーの下に隠れてしまうのでものすごく使い勝手が悪い。


作者への要望リストを見ると「ウィンドウ位置はOSや外部プログラムに任せるべき」みたいなことが書いてあって、対応する気はないらしい。
つーか、OSに任せてる割にはどう見ても固定じゃねーかyp!
と思ってソースを落としてきて眺めてみたら、CreateWindowEx()にCW_USEDEFAULTを渡している。どう見てもOS依存です。(疑って)本当にすいませんでした。

直してやろうじゃないの

で、とりあえずCreateWindowEx()のあたりをデバッガで追っていったら、いきなりウィンドウ作成時の幅と高さがありえなかった。

669: hwnd = CreateWindowEx(exwinmode, appname, appname,
670:                       winmode, CW_USEDEFAULT, CW_USEDEFAULT,
671:                       guess_width, guess_height,
672:                       NULL, NULL, inst, NULL);

このCW_USEDEFAULTってところがウィンドウ位置で、guess_widthとguess_heightが幅と高さなんだが、うちの環境だとguess_width=1225、guess_height=1228とかいうとんでもないサイズになっていた。
もちろん普段使っているときはそんな大きなウィンドウではない。


このguess_widthとguess_heightはどうやって導き出してるのよ、ってことで少し上を見るとこんな感じになってた。

636: /*
637: * Guess some defaults for the window size. This all gets
638: * updated later, so we don't really care too much. However, we
639: * do want the font width/height guesses to correspond to a
640: * large font rather than a small one...
641: */
642: 
643: font_width = 10;
644: font_height = 20;
645: extra_width = 25;
646: extra_height = 28;
647: guess_width = extra_width + font_width * cfg.width;
648: guess_height = extra_height + font_height * cfg.height;
649: {
650:     RECT r;
651:     get_fullscreen_rect(&r);
652:     if (guess_width > r.right - r.left)
653:         guess_width = r.right - r.left;
654:     if (guess_height > r.bottom - r.top)
655:         guess_height = r.bottom - r.top;
656: }

ちょ、フォントサイズ決め打ちかよ……。
しかも、どうせ後で直すんだからいいじゃんか的なコメントまで書いてあるし。


cfg.widthとcfg.heightというのは設定のウィンドウのところにある行と桁のこと。
自分はTerminal 10ptsで120桁60行に設定している。
この値をコードに当てはめて計算すると先ほどの値になるわけだ。


で、画面解像度は1600x1200なんだが、1228がこの1200に収まらないので、OS様は仕方なく一番上から表示しようとしているのではないか、という考えに至った。
早速AppWizardで適当にウィンドウ作って試してみると思ったとおりの動きになった。そしてPuTTYの方も小さめの値を適当に代入してやるとちゃんとタスクバーとはかぶらない位置に表示されるようになった。


原因がわかったところで、このguess_widthとguess_heightに正しい値を入れてやろうと思ったわけだが、ほどなくして手が止まった。
ウィンドウ作成後にはフォントのサイズを基に正しいサイズを計算しているので、そのルーチンをコピればいいんじゃないのと思ってたんだが、フォントが実際に何ピクセルになるかはデバイスコンテキスト(以下DC)がないと計算できなくて、DCを得るにはウィンドウが必要で、まさかデスクトップ使うわけにもいかないし……ぁぁぁぁぁああああああああ/(^o^)\ナンテコッタイ
つーか、それができるようならguessなんちゃらとか決め打ちでやってないよな。考えが甘かったorz


しばらく失意に打ちひしがれてニコニコ動画とか見てたんだが、無ければ作ればいーじゃない、ということでデスクトップからCreateCompatibleDC()でコンパチなメモリDCを作ってやることで解決できた!!
もう3年ぐらいWindowsプログラミングから離れてたので頭の回転が悪いのなんのって。

ウィンドウ位置保存も

悩ましい動作が解消できたことと、久しぶりの窓プロが楽しかったので、調子に乗ってウィンドウ位置保存にも手を出してみることにした。


設定ダイアログはリソースファイルに手を加えなくても、独自のルーチンで何でもかんでも勝手にやってくれるっぽかったので、前後の設定コードをコピって書き換えたらあっさり「Remember window position」というチェックボックスが増えた。


「Remember window position」については設定も保存されるし、ウィンドウ位置の復元についても問題なく実装できたんだが、肝心のウィンドウ位置の保存がどうしても上手くいかない。
どうも設定の更新は設定ダイアログでしか行わない想定になってるらしく、それ以外のタイミングではINIファイルの読み書きに必要なセッション名が取得できないように見える。


ここでまたしても挫折感を覚えて布団に入った。起きたらもう少し頑張ろう……。


翌日起きてご飯を食べたら早速再開。
こうなったらセッションの設定を読み込むタイミングでセッション名をどっかに保存しといてやろう、と一生懸命トレースしまくる。
ただでさえ他人のコードは読みにくいのに、加えてクロスプラットフォームな作りになっているので何がどうなってるのかさっぱりわからない。
セッションを読み込むタイミングも起動時の他にNew sessionとかSaved sessionsとかpagentやコマンドライン引数から来たりと多いし。


そんなこんなで途中3回ぐらい挫折しそうになったけど、諦めないで頑張ってたらついに完成したよ!
ウィンドウ位置の保存と設定はセッション毎で、マルチディスプレイには対応してないけど。


これで明日からの仕事はきっとはかどる……かも。