読者です 読者をやめる 読者になる 読者になる

Poderosa の部分再描画バグ

.NET

鉄塔ターミナル

友達がやっていたのをまねしてみた。これがけっこう良い感じ。

f:id:InoHiro:20080517033523p:image

を読んでいて,自分の使っている Poderosa がカスタム版だったことを唐突に思い出しました.
私も本業の方では Linux がメインで,Poderosa にはお世話になりっぱなしなわけですが,とある理由でソースを弄ったカスタム版 Poderosa を使っています.
その理由というのが,公式フォーラムにもあがっているこれ.

確かに手元の環境でもしばしば目にしていたため,ある日思い立って原因を調べようと途中までデバッグしていたのですが,マルチスレッドに絡む状態遷移問題だと分かって,結局その場しのぎの修正で誤魔化してしまいました.



ソースを軽く読んでみたところ,Poderosa 4.1.0 では行単位で描画バッファの更新領域を管理しています.この更新領域は,主に通信スレッドで更新され,UI スレッドで再描画に使われます.となると,テキストの消去はされるが再描画はされないという状況が起きてしまっているのかな,と.まあ原因についてはあんまりちゃんと調べていないのですが.
手元のカスタム版で使っている応急処置は,更新領域を利用した部分再描画をばっさり諦めるというものでしたが,とりあえずこれで現象は収まりまっています.おかげでそれなりに重くなってしまいましたが.以下の #if false で囲われている部分がその応急処置です.

Poderosa 4.1.0 : poderosa410_1122/Core/CharacterDocumentViewer.cs

//_documentの更新状況を見て適切な領域のControl.Invalidate()を呼ぶ。
//また、コントロールを所有していないスレッドから呼んでもOKなようになっている。
protected void InvalidateEx() {
    bool full_invalidate = true;
    Rectangle r = new Rectangle();
    if(this.IsDisposed) return;

#if false
    if (_document != null && !_document.InvalidatedRegion.InvalidatedAll)
    {
        full_invalidate = false;
        r.X = 0;
        r.Width = this.Width;
        int y1 = _document.InvalidatedRegion.LineIDStart - GetTopLine().ID;
        int y2 = _document.InvalidatedRegion.LineIDEnd + 1 - GetTopLine().ID;
        r.Y = BORDER + (int)(y1 * GetRenderProfile().Pitch.Height);
        r.Height = (int)((y2 - y1) * GetRenderProfile().Pitch.Height) + 1;
    }
#endif

    if(this.InvokeRequired) {
        if(full_invalidate)
            this.Invoke(_delegateInvalidate);
        else {
            _delegateInvalidateRectParam[0] = r;
            this.Invoke(_delegateInvalidateRect, _delegateInvalidateRectParam);
        }
    }
    else {
        if(full_invalidate)
            Invalidate();
        else
            Invalidate(r);
    }
}