ShapeWin − 非矩形ウィンドウサポート Copyright (C) 1998-2001 Software Research Associates, Inc. akira@sra.co.jp 1.はじめに OS/2−PMでは、デスクトップにウィンドウの矩形無しでイメージ等を 表示しようとすると結構苦労します。 ずっと以前にそのようなプログラムを書こうとして挫折したことがあり ます。だいたいは、背景をコピーした上に、イメージを書き込んでやる と、一見デスクトップに直接描画したように見えるのですが、背景部分 が変更されるとばれてしまいます。 その後、Xの Shape Extension のコードを見て、その実装にあきれ、これな らPM上でも実現できるのはないかと思いました。それからかなり経ちまし たが、やっと使用に耐えるものを作ることができましたので、ここに公開い たします。 2.シェイプウィンドウの生成 2.1.ウィンドウクラスの登録 シェイプウィンドウは、カスタムコントロールとして実現しています。使用 するには、クラスの登録が必要です。クラスは以下のように登録してくださ い。 WinRegisterClass( hab, アンカーブロックハンドル ShapeWinName, クラス名 ShapeWinProc, ウィンドウプロシジャ 0L, デフォルトウィンドウスタイル sizeof(PVOID)) ; クラスデータサイズ クラス名、ウィンドウプロシジャは「shapewin.h」で定義されています。ク ラス名は他の名前でも構いません。クラスデータサイズは上の値が必須です。 2.2.ウィンドウの生成 シェイプウィンドウは、通常のウィンドウと同様に「WinCreateWindow」で生 成されます。ただし、以下の点に注意してください。 ・デスクトップ上に非矩形のウィンドウを表示するには、親ウィンドウをデ スクトップにしなければなりません。でなければ、通常の矩形のウィンド ウ内に表示されてしまいますのであまり意味がありません。 ・通常のプログラムではトップレベルウィンドウはフレームウィンドウ(お  よびそのサブクラス)になります。シェイプウィンドウ自体は、フレーム  ウィンドウに相当する機能は持っていませんので、これを直接トップレベ  ルウィンドウにしない方が安全です。 ・以上2点から、プログラム自体のトップレベルウィンドウはフレームウィン  ドウとし、そのフレームウィンドウをオーナとして、シェイプウィンドウ  を生成するのがいいと思います。ただ、このようにする場合には、フレー  ムウィンドウに処理を追加する(フレームウィンドウのサブクラス化)必  要があります。これについては後述します。 ・シェイプウィンドウの生成にはコントロールデータが必須です。コントロー  ルデータについては以下で説明します。 ・シェイプウィンドウのサイズは、コントロールデータで決まります。生成 時のサイズ指定は無視されます。 2.3.シェイプコントロールデータ 生成されるシェイプ(非矩形領域)のサイズ、形、および表示内容は生成時 のコントロールデータで指示します。 表示内容は生成した後で変更することができますが、サイズと形(シェ イプ)は生成時に指示されたもののままで変更できません。 コントロールデータは以下の構造体(shapewin.hで定義)で指示します。 typedef struct _SHAPEWIN { SHORT cx, cy ; シェイプのサイズ HPS hpsMask ; マスクパターン(シェイプ) HPS hpsDraw ; 描画データのソース } SHAPEWIN, *PSHAPEWIN ; hpsMask で指示されるプレゼンテーションスペースに描画された cx × cy サイズのイメージが表示されるウィンドウの形になり、hpsDraw で指示され るプレゼンテーションスペースのデータが、そこに表示されます。 2.4.マスクパターン hpsMask のイメージで、原点(0、0)の色を背景色とみなし、背景色とは 異なった色の部分を描画領域とします。 通常は、メモリPS上で、全体(矩形領域)を背景色で塗りつぶした上に、 描画したいイメージを作るようにします。描画部分に背景色が含まれていな ければ、そのメモリPSをマスクパターン、描画データの両方に指定しても 問題はありません。 サンプルではどれもこのようにしています。 原理的には、使用可能なマスクパターンのサイズに制限はありません。しか し、ある程度大きなものや、かなり入り組んだものを指定するとマシンに多 大な負荷がかかります。 サイズの目安としては、アイコンエディタで取り扱える程度、といった ところだと思います。ただし、単純な図形でしたら、もうすこし大きく ても大丈夫です。複雑なものとしては、os2tile.bmp なんかは、小さい わりにとても重くなります。このようなメッシュ状のパターンは最も苦 手とするところです。os2\bitmapにあるものは大抵そうですね。 2.5.描画データの更新 hpsDraw のプレゼンテーションスペースを介して、変更された描画データを シェイプウィンドウに反映させることができます。 描画領域(hpsDraw) の内容が変更され、それを画面上に反映させるには、 シェイプウィンドウに対して以下のメッセージを送出します。 SHAPEWIN_MSG_UPDATE 画面の更新要求 param1 PRECTL 更新領域 param2 NULL 一部だけが更新されて、余分な描画を避けたい場合には、param1で更新範囲 を指示してください。param1が指定されていなければ(NULLの場合)、画面 全体が再描画されます。シェイプウィンドウの再描画はかなり重い処理にな りますので、変更範囲が小さければ、なるべく更新範囲を指定するようにし た方がいいと思います。 まあ、高速CPUに高性能のビデオボードを使っているなら、全体を再 描画する方が速いかもしれませんが。 3.フレームウィンドウとの組み合わせについて この章の記述は、いわゆるVooDooです。以下の説明は、私の手元の環 境(Warp3/Connect)では動作していますが、他の環境では動作するかどうか わかりません。うちの環境では動かなかったとか、もっとましな方法がある、 という方は御連絡ください。 シェイプウィンドウを使用する時は、デスクトップ上に(他のウィンドウの 中ではなく)直接描画したいからだと思います。しかし、シェイプウィンド ウ自体にはフレーム相当の機能は含まれていませんので、シェイプウィンド ウをトップレベルウィンドウにするのは危険です。このため、 親ウィンドウ デスクトップ オーナウィンドウ フレームウィンドウ としてシェイプウィンドウを作るのが安全でいいかと思います。 このようにして作成されたフレームウィンドウ、シェイプウィンドウには親 子関係がないために、通常のフレーム/クライアントで行なわれる制御が一 部効かなくなります。これらの制御を有効にするには、フレームウィンドウ の処理をカスタマイズ(サブクラス化)する必要があります。 また、このような構成でウィンドウを作ると、画面上には、フレームウィン ドウとシェイプウィンドウの両方が表示されることになります。余分なフレー ムウィンドウの表示無しで、シェイプウィンドウだけが表示されているよう に見せるにも、フレームウィンドウの処理をカスタマイズ(サブクラス化) する必要があります。 3.1.フレーム制御メッセージの転送 フレームウィンドウの位置、サイズ、Zオーダ等が変化する場合には、 WM_ADJUSTWINDOWPOS 変更通知 WM_WINDOWPOSCHANGED 完了通知 のメッセージが発生します。シェイプウィンドウの位置やZオーダをフレー ムウィンドウに追従させるには、これらのメッセージの延長で、シェイプウィン ドウの位置やZオーダを制御するとよいようです。具体的には case WM_ADJUSTWINDOWPOS : pswp = (PSWP) PVOIDFROMMP(mp1) ; WinSetWindowPos(hwndShape, pswp->hwndInsetBehind, pswp->x, pswp->y, pswp->cx, pswp->cy, pswp->fl) ; return デフォルト処理 とか case WM_WINDOWPOSCHANGED : pswp = (PSWP) PVOIDFROMMP(mp1) ; WinSetWindowPos(hwndShape, pswp->hwndInsetBehind, pswp->x, pswp->y, pswp->cx, pswp->cy, pswp->fl) ; return デフォルト処理 という形態です。なお、WINDOWPOSCHANGED の方は、フレームウィンドウを非 表示にした場合(次の節)には使えません。 詳しくはサンプルコードを御覧ください。 3.2.見えないフレームウィンドウ フレームウィンドウ、シェイプウィンドウの両方を作り、シェイプウィンド ウだけが表示されるようにするために、フレームウィンドウを非表示にする ことができます。 これには、前出の「WM_ADJUSTWINDOWPOS」メッセージをフックし、SWPデー タを変更します。フレームを非表示にするには case WM_ADJUSTWINDOWPOS : pswp = (PSWP) PVOIDFROMMP(mp1) ; WinSetWindowPos(hwndShape, pswp->hwndInsetBehind, pswp->x, pswp->y, pswp->cx, pswp->cy, pswp->fl) ; pswp->fl &= ~SWP_SHOW ; return デフォルト処理 とします。またウィンドウサイズを固定するには、 case WM_ADJUSTWINDOWPOS : pswp = (PSWP) PVOIDFROMMP(mp1) ; WinSetWindowPos(hwndShape, pswp->hwndInsetBehind, pswp->x, pswp->y, pswp->cx, pswp->cy, pswp->fl) ; pswp->cx = 固定サイズ ; pswp->cy = 固定サイズ ; pswp->fl &= ~SWP_SHOW ; return デフォルト処理 として制御できます。 詳しくはサンプルコード(trbitmap.c、tranime.c)を御覧ください。 3.3.キー、マウス事象の通知 シェイプウィンドウで発生したキー、マウス関連のイベント(メッセージ) はオーナウィンドウに転送されます。そこで、キーやマウスに対する処理を 実装するには、フレームにそのようなコードを組み込む必要があります。 ここで注意して欲しい点は、マウス関連のメッセージでは多くの場合マウス の位置が一緒に通知されるようになっていますが、シェイプウィンドウを使っ た場合には、マウス位置がシェイプウィンドウ相対になっているため、フレー ムウィンドウでその位置を参照しても無意味になっている、という点です。 処理の都合でマウス位置を必要とする場合には別途「WinQueryPointerPos」 等で求めるようにしてください。 詳しくはサンプルコード(trbitmap.c、treyes.c)を御覧ください。