parkの雑記帳
2009-12-26 [Sat]
・ [ハードウェア] 新型トラックポイントキーボード届いた
Twitterでとある人にそそのかされてLenovoのUSBトラックポイントキーボード (ThinkPad USB Keyboard with TrackPoint) を買ってしまいました。 これまでもその前身であるところnトラベルキーボードを愛用していたのですが、 最近マウス操作の中ボタンが壊れたりキーの印字が消えたりとかなりボロボロになっていたので ついポチってしまったわけです。
以下感想などをすこし。
ハードウェア面
キータッチは良い感じです。前身のトラベルキーボードは値段に対してかなり安っぽい感じでしたが 今度のはしっかりした感じがします。ただ、キーの形状が変わったため、人によっては 誤って隣のキーを押してしまうことが増えるかもしれません。
キー配列はT400sと同じです。Escと間違えてF1をおしてしまう事故がなくなったのはすばらしいです。 ただ、その関係でF1-F12キーの位置が変わっているので押し間違いがあるかも。
特殊キーはThinkPad専用っぽいのは使えませんでした。 使えるのは音量キー・スタンバイ・ブラウザ戻る進む・メディアプレーヤー操作でした。あと、ブラウザ戻る進むがAlt-左、Alt-右を送出する困った仕様はトラベルキーボードと同じでした。
ソフトウェア面
いつものSynapticsのやつじゃありません。 分解してみた人のレポートによると、 ThinkPadのキーボードユニットをLITEONのチップで変換しているとのことです。 トラベルキーボードではトラックポイントとキー部分とで別々に接続された基板が使われていたので、 内部的には全くちがうものになっているのでしょう。 それで、PCにインストールするプログラムもLITEONの作ったものっぽかったです。
使ってみた感じ、非常に残念。一部のプログラムにはスクロール開始時にCtrl-Kを送ったり、 Tweenをはじめとした.NET Frameworkのプログラムでスクロールできなかったり、なんかもう別物。 トラベルキーボードに戻そうかと本気で考えるぐらいのできでした。
まとめ
トラックポイントの使えるキーボードが今までよりはるかに安く入手できるというのは 非常に喜ばしいことなんですが、ソフトウェアが残念でなりません。 でもまあ、キータッチとかは良いので、6,000円ぐらいだしポチってしまっても 大損はしないんじゃないかなと思います。
・ [ハードウェア][ソフトウェア] 新型トラックポイントキーボードのスクロール改善
前のエントリでも書いた、Tweenをはじめとした.NET Frameworkのプログラムでスクロールできない問題。 これをアドホックな解決策でスクロールできるようにする方法を見つけてプログラム書きました。
FixSkmz.zip ※無保証、自己責任でお願いします。
原理とか
Spy++ (Visual Studio に付いてくる、ウィンドウメッセージとかを監視したりするツール) で ウィンドウメッセージを見てみると、WM_VSCROLL, WM_HSCROLL をPostしているようでした。 しかしながら、スクロールがされません。 試しに任意のウィンドウにPostMessage()するプログラムを作ってWM_VSCROLLをPostすると なんとスクロールされます。 この違いが何であるかをよく見てみると、トラックポイントの操作ではlParamが非NULLなのに対して 自分のPostMessage()ではNULLでした。 だったら、メッセージを細工してlParamをNULLにしてしまえば良い、という発想です。
実際にやるとなると、SetWindowHookEx()でWH_GETMESSAGEフックを仕込むことになりました。 ここでWM_VSCROLL, WM_HSCROLLが来た場合lParamをNULLにしてしまいます。 でもなんか怖いので.NET Frameworkのプログラムだけを対象にすることにしました。 どうやって判断するかは手抜きで、mscoree.dllをロードしているか否かを判断基準にしました。
実際に作ってみて動かす段階になっていざ試してみると完全に期待通りの動作。 なんかもう拍子抜けでした。
話は変わりますが、トラックポイントのスクロール、ちょっと感度が高すぎる気がします。縦方向だけにスクロールとか横方向だけにスクロールとかがやりづらい。まあそのうち改善されるといいんですが...
2009-12-14 [Mon]
・ [ソフトウェア] Tween改造 -Replyをちゃんとたどろう
前回のエントリで偉そうに書いていたのは初期設定でしか動きませんでした...
というのも、発言リストのソート順とかに依存していたみたいで、ちょっといじるとアラマうごかないわ。となってしまいましたとさ。
ということで GoInReplyToPost() メソッドを修正。
Private Sub GoInReplyToPost()
If _curPost IsNot Nothing AndAlso _curPost.InReplyToUser IsNot Nothing AndAlso _curPost.InReplyToId > 0 Then
If _statuses.ContainsKey(_curPost.InReplyToId) Then
Dim idx As Integer = _statuses.Tabs(_curTab.Text).IndexOf(_curPost.InReplyToId)
If idx = -1 Then
Dim repPost As PostClass = _statuses.Item(_curPost.InReplyToId)
MessageBox.Show(repPost.Name + " / " + repPost.Nickname + " (" + repPost.PDate.ToString() + ")" + Environment.NewLine + repPost.Data)
Else
SelectListItem(_curList, idx)
_curList.EnsureVisible(idx)
End If
Else
OpenUriAsync("http://twitter.com/" + _curPost.InReplyToUser + "/statuses/" + _curPost.InReplyToId.ToString())
End If
End If
End Sub
・ [ソフトウェア] Tween改造 -ビルドエラーを無視しない
なんかシリーズみたいになってきました。
Tweenを改造して遊ぶにはソースコードをダウンロードして、手元のVisual Studio (2005以降) で適当にいじってビルドします。
しかし、環境によってはビルドしたときに "c:\Program Files\Microsoft.NET\SDK\v2.0\Bin\sgen.exe" が見つからない旨のエラーが表示されると思います。これはおそらくメイン制作者さんの.NET Framework SDKのインストールディレクトリをプロジェクトに直打ちしているから悪いのでしょう。私の環境にはこんなディレクトリありませんし。
実は、Visual Studioのプロジェクトでは $(FrameworkSDKDir) というマクロを使うことでこの問題を回避できます。まあマクロ名通りな内容なんですが。プロジェクトのプロパティからビルドイベントでこのへんを適当に編集するとエラーが無くなるはずです。
・ [ソフトウェア] Tween改造 -Replyをたどって戻ろう、タブをまたいで
in_reply_to をたどる機能を付けた私家版を使っていて思いました。元発言の方向にたどるのも良いけど時系列順でもたどりたい。
ということで、in_reply_to をたどった場合に、たどったのを戻れるようにしてみました。ついでに違うタブに発言があってもたどれるようにしました。スタックにIDとtabを積んでいるだけなんですけどね。
Private Structure ReplyChain
Public OriginalId As Long
Public InReplyToId As Long
Public OriginalTab As TabPage
Sub New(ByVal originalId As Long, ByVal inReplyToId As Long, ByVal originalTab As TabPage)
Me.OriginalId = originalId
Me.InReplyToId = inReplyToId
Me.OriginalTab = originalTab
End Sub
End Structure
Private replyChains As Stack(Of ReplyChain)
Private Sub GoInReplyToPost()
If _curPost IsNot Nothing AndAlso _curPost.InReplyToUser IsNot Nothing AndAlso _curPost.InReplyToId > 0 Then
If _statuses.ContainsKey(_curPost.InReplyToId) Then
Dim tab As TabPage = _curTab
Dim idx As Integer = _statuses.Tabs(tab.Text).IndexOf(_curPost.InReplyToId)
If idx = -1 Then
For Each tab In ListTab.TabPages
idx = _statuses.Tabs(tab.Text).IndexOf(_curPost.InReplyToId)
If idx <> -1 Then
Exit For
End If
Next
End If
If idx = -1 Then
Dim repPost As PostClass = _statuses.Item(_curPost.InReplyToId)
MessageBox.Show(repPost.Name + " / " + repPost.Nickname + " (" + repPost.PDate.ToString() + ")" + Environment.NewLine + repPost.Data)
Exit Sub
End If
If replyChains Is Nothing OrElse (replyChains.Count > 0 AndAlso replyChains.Peek().InReplyToId <> _curPost.Id) Then
replyChains = New Stack(Of ReplyChain)
End If
replyChains.Push(New ReplyChain(_curPost.Id, _curPost.InReplyToId, _curTab))
If tab IsNot _curTab Then
ListTab.SelectTab(tab)
End If
SelectListItem(_curList, idx)
_curList.EnsureVisible(idx)
Else
OpenUriAsync("http://twitter.com/" + _curPost.InReplyToUser + "/statuses/" + _curPost.InReplyToId.ToString())
End If
End If
End Sub
Private Sub GoBackInReplyToPost()
If replyChains Is Nothing OrElse replyChains.Count < 1 Then
Exit Sub
End If
Dim chainHead As ReplyChain = replyChains.Pop()
If chainHead.InReplyToId = _curPost.Id Then
Dim idx As Integer = _statuses.Tabs(chainHead.OriginalTab.Text).IndexOf(chainHead.OriginalId)
If idx = -1 Then
replyChains = Nothing
Else
Try
ListTab.SelectTab(chainHead.OriginalTab)
Catch ex As Exception
replyChains = Nothing
End Try
SelectListItem(_curList, idx)
_curList.EnsureVisible(idx)
End If
Else
replyChains = Nothing
End If
End Sub
そろそろコメント無しだとつらいレベルのコード片になってきましたw
手元の私家版では GoInReplyToPost() を I キーで、GoBackInReplyToPost() を Shift-I キーで呼び出すようにしています。なかなか快適。
本当はコード片ではなくそのまま使える形にしたいんですがいろいろ面倒なので...
2009-12-12 [Sat]
・ [プログラミング] Tween改造 -Replyをたどろう
[追記] 以下のコードにはバグがあるかもしれません。必要なら後で修正します。
[追記] 修正版を書きました。
Twitterのtweetには in_reply_to というものがあって、これで「どの発言に対するreplyか」というのが分かるようになっています。Webで見たときの __park宛(英語だと in reply to __park)ってやつですね。
で、TweenではこれをCtrl+Iで見ることができるんですが、これだとメッセージボックスで元発言が出てくるのでたどることができません。発言をたどるものとして左右キーがありますが、これでは in_reply_to と関係ないものまでたどってしまい分からなくなってしまいます。
ということで、I キーを押すことで in_reply_to のみをたどる機能をつけてみようとおもいました。
とりあえず実際に動かしたらうまくいったんですがよく見ると古いバージョン(0.7.5.0)のソースコードだった!ということで最新の物をチェックアウトしようとしたらでてこない。リポジトリ変わってたのね。ということで取ってきたらビルドができない。ファイル追加忘れがあったようで…
ということで、Tween.vbに新しく加えたメソッドを書いておきます。これを I キーが押されたときに呼び出せば動くはず…
Private Sub GoInReplyToPost()
If _curPost IsNot Nothing AndAlso _curPost.InReplyToUser IsNot Nothing AndAlso _curPost.InReplyToId > 0 Then
If _statuses.ContainsKey(_curPost.InReplyToId) Then
Dim i As Integer = _curList.SelectedIndices(0)
While True
Dim post As PostClass = _statuses.Item(_curTab.Text, i)
If post.Id < _curPost.InReplyToId Then
Dim repPost As PostClass = _statuses.Item(_curPost.InReplyToId)
MessageBox.Show(repPost.Name + " / " + repPost.Nickname + " (" + repPost.PDate.ToString() + ")" + Environment.NewLine + repPost.Data)
Exit While
End If
If post.Id = _curPost.InReplyToId Then
SelectListItem(_curList, i)
_curList.EnsureVisible(i)
Exit While
End If
i = i - 1
End While
Else
OpenUriAsync("http://twitter.com/" + _curPost.InReplyToUser + "/statuses/" + _curPost.InReplyToId.ToString())
End If
End If
End Sub
2009-11-01 [Sun]
・ [プログラミング] Tween改造
最近Twitterのサーバで画像を縮小してくれなくなりました。そのためアップロードされた画像のサイズそのままを取得することになってしまいます。
そこで問題になるのが縮小の際のリサイズの処理です。
Tweenでは System.Windows.Forms.PictureBox にそのまま任せているのですが、PictureBox のデフォルトの処理が画質重視ではなく、そのため画像によってはジャギーが出てしまいます。
そこで、以下のようなコード断片を Tween.vb に追加し、UserPicture コントロールの Paint イベントのハンドラとして追加しました。
Private Sub UserPicture_Paint(ByVal sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles UserPicture.Paint
If e.Graphics.InterpolationMode <> Drawing2D.InterpolationMode.HighQualityBicubic Then
e.Graphics.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBicubic
UserPicture.GetType().GetMethod("OnPaint", BindingFlags.NonPublic Or BindingFlags.Instance).Invoke(UserPicture, New Object() {e})
End If
End Sub
結果はこの通り。

手法がすごく汚いとは自覚してます。本当は PictureBox のサブクラスを作って OnPaint メソッドを override するべきなんでしょうが。
・ [プログラミング] Tween改造のちゃんとしたやりかた
ユーザーコントロール作ったときに既存クラスのサブクラスであればスーパークラスの属性とかをそのままフォームエディタで使えたということに今更気付いた。
ということで、PictureBox を継承したやつを作ってユーザーコントロールとして追加してやればいいようで。
public partial class MyPictureBox : PictureBox
{
public MyPictureBox()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
base.OnPaint(pe);
}
}
こんな感じで。
ユーザーコントロールをフォームエディタで使うときには、コントロールを選ぶツールボックスの所で右クリックして「アイテムの追加」から探すんだけれど、このときにコントロールの入ったアセンブリ (.dll, .exe) を参照ボタンから追加してやらないといけないらしい。
2009-09-17 [Thu]
・ [プログラミング] C#でTwitterライブラリ (OAuth対応)
タイトルのとおり、C#でTwitterライブラリのような物を書いてみた。OAuthにもいちおう対応させてみたり。
ろくにテストをしていないです。
- Account Methods
- Notification Methods
- Block Methods
- Saved Searches Methods
このへんはまだ書いていない。
TwitterOAuth oAuth = new TwitterOAuth(consumerKey, consumerSecret);
string authUrl = oAuth.GetAuthLink();
string code = "hoge";// (本当はユーザに authUrl にアクセスしてもらって、認証コードを入れてもらう);
oAuth.VerifyOAuthByPinCode(authUrl, code);
// oAuth.Token, oAuth.TokenSecret を保存。
// 次回は new TwitterOAuth(consumerKey, consumerSecret, token, tokenSecret)
TwitterApi api = new TwitterApi(oAuth);
とかやればTwitter APIが使えるようになって、
TwitterStatus[] timeline = api.GetFriendsTimeLine();
foreach (TwitterStatus tweet in timeline)
System.Console.WriteLine(tweet);
みたいに使えるんじゃないだろうか。
これを使うような危篤、じゃない奇特な人はいないだろうけれど、無保証なので注意。あと英語が間違っていても笑って赦して。
2009-09-07 [Mon]
・ [プログラミング] LEDClock
Twitterで信頼できないexeファイル開くなとかたいそうなことを言ってしまったので貼ったexeファイルをソースコード付きで公開してみる。
ledclock.zip MD5: 3097c35a1e5067cdee61c09fdcaf73c5
まあどうせHTTPSじゃないからMD5書いてもあまり意味はないんだけれど。信頼するか否かの判断は慎重にどうぞ。
2009-09-04 [Fri]
・ [プログラミング] Mono 2.4.2.3 にバグ?
C#には ?? 演算子という便利な物があって、こいつは前者が左辺を評価し、nullでなければ左辺を値とし、nullであれば右辺を評価して値とする。
元々はC# 2.0でNullable型が導入された際に追加された演算子で、Nullable型を非Nullable型に代入する際にデフォルト値を与えるために使うものだけれど、何かと他の場所でも使える。
例えば
string var = SomeFunction() ?? "failed";
みたいな使い方ができたりする。
プログラミングをしているときにこれを使おうとしていたんだが、「左辺が非nullなら右辺は評価されないんだっけ?」と疑問を持った。ちなみに正解は「評価されない」であっていた。そこで、試すのにFreeBSDのmonoを使おうとしたのが間違いの始まりだった。
using System;
public class NullCoalescing
{
public static void SuppressWarnings(params object[] o) { }
public static void Main()
{
string a, b;
a = "not changed";
b = null ?? (a = "changed");
Console.WriteLine("a = \"not changed\";");
Console.WriteLine("b = null ?? (a = \"changed\");");
Console.WriteLine("result: a == \"" + a + "\"");
Console.WriteLine();
a = "not changed";
b = "" ?? (a = "changed");
Console.WriteLine("a = \"not changed\";");
Console.WriteLine("b = \"\" ?? (a = \"changed\");");
Console.WriteLine("result: a == \"" + a + "\"");
SuppressWarnings(a, b);
}
}
試しにこんなコードを書いてみたらとんでもないことが起こった。
$ gmcs NullCoalescing.cs $ ./NullCoalescing.exe a = "not changed"; b = null ?? (a = "changed"); result: a == "changed" a = "not changed"; b = "" ?? (a = "changed"); result: a == "changed"
え?右辺評価されてる!?
おかしいなと思ってVisual C#で確認してみる
>csc NullCoalescing.cs >NullCoalescing.exe a = "not changed"; b = null ?? (a = "changed"); result: a == "changed" a = "not changed"; b = "" ?? (a = "changed"); result: a == "not changed"
え?評価されてない!?
あまりにもおかしいので別の型で試したら右辺は評価されないし、a, bをobject型として宣言したら右辺評価されなくなるし。
結局、string型かつ左辺が定数の場合には再現したけれどそれ以外では再現しなかったという。最適化関係でバグってるのかしら。
ちなみにおかしかったのはgmcsで、cscでコンパイルしたバイナリは.NET Frameworkでもmonoでも正常に動いた。

・ mitty [おつー。 参加したかったけれど、掛かっている先生から遠出は避けた方が良い、とのお達しがあったので涙を飲みました。 ..]