Akira's Commentary


やっとウィンドウ

ここまで準備してきて やっとシェイプウィンドウを作ることができます。

デスクトップに非矩形のウィンドウを表示するには、 カスタマイズしたフレームウィンドウと、 シェイプウィンドウとを組み合わせて使います。

まずは、カスタマイズしたフレームウィンドウを作ります。 このような非標準のフレームウィンドウを作るには、 WinCreateWindow で ウィンドウクラス「WC_FRAME」のウィンドウを生成します。 この場合、パラメタ/フラグはフレームコントロールデータ

FRAMECDATA  fcd ;

に設定して渡す形態になります。

フレームウィンドウが生成されたら、 フレームウィンドウをサブクラス化してカスタマイズします。 カスタマイズされたウィンドウプロシジャについては後で説明します。

---- フレームウィンドウの生成 ここから ----
/*
 * createFrame - create frame window
 */

static  PFNWP   pfnFrame ;
static MRESULT EXPENTRY procFrame(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2) ;

static  void    createFrame(HAB hab)
{
    FRAMECDATA  fcd    ;

    memset(&fcd, 0, sizeof(fcd)) ;
    fcd.cb = sizeof(fcd) ;
    fcd.flCreateFlags = (FCF_TASKLIST | FCF_ICON) ;
    fcd.hmodResources = NULLHANDLE ;
    fcd.idResources   = ID_ROMY    ;   

    hwndFrame = WinCreateWindow(
            HWND_DESKTOP,           /* Parent window handle     */
            WC_FRAME,               /* Frame Window Class       */
            ProgramName,            /* as Title                 */
            0,                      /* Window Style             */
            0, 0, 0, 0,             /* Position & size          */
            NULLHANDLE,             /* Owner Window             */
            HWND_TOP,               /* Z-Order                  */
            0,                      /* Window ID                */
            &fcd,                   /* Control Data             */
            NULL) ;                 /* Presentation Parameter   */

    if (hwndFrame == NULLHANDLE) {
        return ;
    }

    pfnFrame = WinSubclassWindow(hwndFrame, procFrame) ;

    WinSendMsg(hwndFrame, WM_SETICON, 
        MPFROMP(WinLoadPointer(HWND_DESKTOP, NULLHANDLE, ID_ROMY)), NULL) ;
}
---- フレームウィンドウの生成 ここまで ----

フレームウィンドウが無事生成されたら、 そのフレームウィンドウをオーナーウィンドウとして シェイプウィンドウを作ります。 シェイプウィンドウを作るには、

フレームウィンドウオーナーとして指示します
ビットマップ サイズも求めます
メモリPS マスク/描画データ

を必要としますので、 これらがちゃんと用意されているかをチェックしておきます。 データが用意されていれば、ビットマップのサイズ、 メモリPSをコントロールデータに設定して シェイプウィンドウを生成します。

ここで、メモリPSには、ビットマップのロード関数によって、 ビットマップデータが書き込まれた状態になっています。 それが生成される非矩形ウィンドウの形状(シェイプ)を決定します。

---- シェイプウィンドウの生成 ここから ----
/*
 * createShape - create shape window
 */

static  void    createShape(HAB hab)
{
    BITMAPINFOHEADER2   bmi ;
    SHAPEWIN    shpctrl ;
    
    if (hwndFrame == NULLHANDLE) {
        return ;
    }
    if (hpsBitmap == NULLHANDLE || hbmBitmap == NULLHANDLE) {
        return ;
    }
    bmi.cbFix = sizeof(bmi) ;
    GpiQueryBitmapInfoHeader(hbmBitmap, &bmi) ;
    
    /*
     * Register Window Class
     */
     
    WinRegisterClass(hab, ShapeWinName, ShapeWinProc, 0L, sizeof(PVOID)) ;

    /*
     * Create Image Window
     */

    shpctrl.cx = bmi.cx ;
    shpctrl.cy = bmi.cy ;
    shpctrl.hpsDraw = hpsBitmap ;
    shpctrl.hpsMask = hpsBitmap ;
    
    hwndShape = WinCreateWindow(
            HWND_DESKTOP,           /* Parent Window    */
            ShapeWinName,           /* Window Class     */
	    NULL,                   /* Window Text      */
	    0,                      /* Window Style     */
	    0, 0, 0, 0,             /* Pos & Size       */
	    hwndFrame,              /* Owner Window     */
	    HWND_TOP,               /* Z-Order          */
	    0,                      /* Window ID        */
	    &shpctrl,               /* Control Data     */
	    NULL) ;                 /* Pres. Param.     */
    
    if (hwndShape == NULLHANDLE) {
        return ;
    }
}
---- シェイプウィンドウの生成 ここまで ----

ウィンドウの生成関数ができたところで、先ほど後回しにしていた カスタムフレームウィンドウプロシジャを用意します。 とりあえずはシェイプウィンドウの実現に必要な機能だけを 組み込んでおきます。

フレームのサイズ変更を禁止し、 フレームウィンドウを不可視に設定しています。

---- フレームウィンドウプロシジャ ここから ----
/*
 * procFrame - sub-classed frame window procedure
 */

static MRESULT EXPENTRY procFrame(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
    PSWP    pswp ;
    
    TRACE("frame %08x %08x %08x\n", msg, mp1, mp2) ;
    
    switch (msg) {
        
    case WM_ADJUSTWINDOWPOS :
        pswp = (PSWP) PVOIDFROMMP(mp1) ;
	WinSetWindowPos(hwndShape, pswp->hwndInsertBehind,
        	    pswp->x, pswp->y, pswp->cx, pswp->cy, pswp->fl) ;
	pswp->fl &= ~SWP_SHOW ;
	pswp->fl |=  SWP_HIDE ;
	return (*pfnFrame) (hwnd, msg, mp1, mp2) ;
    }
    return (*pfnFrame) (hwnd, msg, mp1, mp2) ;
}
---- フレームウィンドウプロシジャ ここまで ----

以上のコードでシェイプウィンドウが使用可能になります。 しかし、ウィンドウが不可視で作成されていますので、 これを組み込んでもまだ画面は出てきません。 これではさみしいので、初期表示処理を組み込みましょう。

シェイプウィンドウを可視にすれば絵は出ますが、 そのままでは左下隅に表示されます(初期位置が0、0なので)。 それではあまり格好よくないので、マウス位置、 あるいはコマンドラインで指示された位置に表示されるようにします。

初期表示位置はコマンドラインから取得します。 コマンドラインでの指示が無かった場合には、 その時点のマウス位置を使用します。 初期表示位置は以下の関数で求められ、 結果は構造体「SWP」 に(位置だけですが)格納されます。

コマンドラインで位置、サイズの両方が指定できる場合には、 SWP を使うのが便利なので、今回のプログラムのようなサイズ 指定が無い場合にも無駄にSWP を使ってしまっています。

---- 表示位置の取得 ここから ----
/*
 * initPos - initial Positions from argument (or default)
 */

static  void    initPos(int ac, char *av[], PSWP swp)
{
    POINTL  ptCur  ;
    int     i      ;

    TRACE("InitPos\n") ;
    
    /*
     * default position from Pointer
     */

    WinQueryPointerPos(HWND_DESKTOP, &ptCur) ;
    
    swp->x = ptCur.x ;
    swp->y = ptCur.y ;

    /*
     * change them if option specified
     */

    for (i = 1 ; i < ac ; i++) {
        if (av[i][0] != '-') {
	    continue ;
	}
	switch (av[i][1]) {
	case 'x' :
	case 'X' :
	    if (av[i][2] != '\0') {
	        swp->x = atoi(&av[i][2]) ;
	    } else if ((i + 1) < ac) {
	        swp->x = atoi(av[i+=1]) ;
	    }
	    break ;
	case 'y' :
	case 'Y' :
	    if (av[i][2] != '\0') {
	        swp->y = atoi(&av[i][2]) ;
	    } else if ((i + 1) < ac) {
	        swp->y = atoi(av[i+=1]) ;
	    }
	    break ;
	}
    }
}
---- 表示位置の取得 ここまで ----

初期表示は、ウィンドウ生成後、メッセージループ開始前に行ないます。 一応、ウィンドウが画面からはみ出さないように調整した上で、 フレーム、シェイプウィンドウの位置/可視属性を指示します。

---- 初期表示 ここから ----
/*
 * placeStartup - set startup position
 */

static  void    placeStartup(PSWP swp)
{
    SWP     swpScreen ;
    BITMAPINFOHEADER2   bmi ;
    SHORT   x, y ;
    
    if (hbmBitmap == NULLHANDLE || hwndFrame == NULLHANDLE || hwndShape == NULLHANDLE) {
        return ;
    }
    
    x = swp->x ;
    y = swp->y ;
    
    bmi.cbFix = sizeof(bmi) ;
    GpiQueryBitmapInfoHeader(hbmBitmap, &bmi) ;

    WinQueryWindowPos(HWND_DESKTOP, &swpScreen) ;

    if ((x + bmi.cx) > swpScreen.cx) {
        x = swpScreen.cx - bmi.cx ;
    }
    if ((y + bmi.cy) > swpScreen.cy) {
        y = swpScreen.cy - bmi.cy ;
    }
    
    WinSetWindowPos(hwndFrame, NULLHANDLE, 
                    x, y, bmi.cx, bmi.cy, (SWP_MOVE | SWP_SIZE | SWP_HIDE)) ;
    WinSetWindowPos(hwndShape, NULLHANDLE, 
                    x, y, bmi.cx, bmi.cy, (SWP_MOVE | SWP_SIZE | SWP_SHOW)) ;
}
---- 初期表示 ここまで ----

追加した関数をメイン関数に組み込んで、 やっと(長かった)Romyさんの自画像が表示されるようになります。 ただ、白目を剥いているだけですし、何が動くわけでもありませんが。 それに終了制御もついていません。 止めるにはタスクリストから指示しなければなりません。 これではあんまりですので、 次には制御用のメニューなんぞを入れてみることにします。

---- メイン関数 ここから ----
/*
 * main - program start here
 */

int     main(int ac, char *av[])
{
    HAB     hab  ;
    HMQ     hmq  ;
    QMSG    qmsg ;
    SWP     swp  ;
    
    myname(av[0]) ;
    _wildcard(&ac, &av) ;

    hab = WinInitialize(0) ;
    hmq = WinCreateMsgQueue(hab, 0) ;

    initPos(ac, av, &swp) ;

    loadBitmap(hab) ;
    
    if (hbmBitmap == NULLHANDLE) {
        trMessage("failed to load bitmap") ;
        freeBitmap() ;
        WinDestroyMsgQueue(hmq) ;
        WinTerminate(hab) ;
	return 1 ;
    }
    
    /*
     * Start Window Processing
     */

    createFrame(hab) ;
    createShape(hab) ;
 
    if (hwndFrame == NULLHANDLE || hwndShape == NULLHANDLE) {    
        trMessage("failed to create windows") ;
 	if (hwndFrame != NULLHANDLE) WinDestroyWindow(hwndFrame) ;
	if (hwndShape != NULLHANDLE) WinDestroyWindow(hwndShape) ;
        freeBitmap() ;
        WinDestroyMsgQueue(hmq) ;
        WinTerminate(hab) ;
 	return 1 ;
    }
     
    placeStartup(&swp) ;
    
    while (WinGetMsg(hab, &qmsg, 0, 0, 0)) {
        WinDispatchMsg(hab, &qmsg) ;
    }

    /*
     * dispose resources
     */
    
    WinDestroyWindow(hwndFrame)  ;
    WinDestroyWindow(hwndShape) ;

    freeBitmap() ;
    
    WinDestroyMsgQueue(hmq) ;
    WinTerminate(hab) ;
    
    return 0 ; 
}
---- メイン関数 ここまで ----