Akira's Commentary


マウスを見つめて

それでは最後にRomyさんに マウスを見つめていただくとしましょう。

目玉を動かす処理は一定時間毎に

  1. マウスの位置を求め
  2. それが動いていれば(以前の位置と違っていれば)
  3. 以前の目玉をクリアして
  4. 新しい位置に目玉を描画する

を繰り返して実行します。

一定時間毎に処理を実行させるにタイマを使うのが一番簡単です。 メイン関数でメッセージループ開始前に タイマ(周期タイマ)を起動しておきます。

WinStartTimer(hab, hwndFrame, 1, 200) ;

今回はフレームウィンドウにアプリケーションコードを入れますので タイマメッセージの送り先はフレームウィンドウになります。 これで200ms毎にフレームウィンドウに タイマメッセージが送出されることになります。

フレームウィンドウプロシジャでは、 タイマメッセージを受け取ったなら、 目玉の描画関数を呼び出すようにします。

    case WM_TIMER :
        drawEyes() ;
	return (MRESULT) 0 ;

これで描画の準備は整いました。 あとは実際の描画関数(drawEyes)を定義するだけです。

マウスの位置は

WinQueryPointerPos(HWND_DESKTOP, &pt) ;

で得られます。ただしこれはデスクトップウィンドウ上での マウス位置ですので、ビットマップ相対の座標に変換します。 今回はフレームウィンドウがビットマップと 同一位置/サイズになっていますので、 フレームウィンドウの相対座標に変換します。

WinMapWindowPoints(HWND_DESKTOP, hwndFrame, &pt, 1) ;

前回の処理時点以降にマウスが動いたかどうかは、 前回のマウス位置を保存しておき、 新しいマウス位置が前回の位置と異なっているかどうかで 判定します。 マウスが移動していなければ描画は不要ですので、 即刻復帰してしまいます。

    if (pt.x == ptLast.x && pt.y == ptLast.y) {
        return ;
    }
    ptLast.x = pt.x ;
    ptLast.y = pt.y ;

マウスが移動していたなら、最初に以前の目玉をクリアしておきます。 背景色で目玉を描画すればクリアしたことになります。 目玉は単純な円ですので、GpiFullArcで描画します。 これを使うには円弧描画用のパラメタを設定しておく必要があります。 このパラメタは、(以前の位置での)目玉のクリア、 (新たな位置での)目玉の描画で共通に使用します。

    /*
     * prepare arc params for eye balls
     */
     
    arcParam.lP = EYE_RI ;
    arcParam.lQ = EYE_RI ;
    arcParam.lR = 0 ;
    arcParam.lS = 0 ;
    GpiSetArcParams(hpsBitmap, &arcParam) ;

目玉のクリアするためには、以前の目玉の位置を保存しておき、 その位置に背景色で目玉を描画します。

    /*
     * clear last position
     */

    GpiSetColor(hpsBitmap, CLR_BACK) ;
    GpiMove(hpsBitmap, &ptLastL) ;
    GpiFullArc(hpsBitmap, DRO_FILL, (FIXED) 0x10000) ;
    GpiMove(hpsBitmap, &ptLastR) ;
    GpiFullArc(hpsBitmap, DRO_FILL, (FIXED) 0x10000) ;

これで以前の目玉がクリアされましたので、 新しい目玉の位置を計算して描画するとしましょう。 目玉の位置の計算は、calPos で行ないます。 これは、目玉の中心、マウス位置から、三角関数を使って、 目玉の位置を求めるものです。 これで左右の目玉の位置を求めます。

    /*
     * calc. new positions
     */
    
    calcPos(&ptCentL, &pt, &ptLastL) ;
    calcPos(&ptCentR, &pt, &ptLastR) ;

そして得られた位置に前景色で目玉を描画します。

    /*
     * draw on new positions
     */
     
    GpiSetColor(hpsBitmap, CLR_BALL) ;
    GpiMove(hpsBitmap, &ptLastL) ;
    GpiFullArc(hpsBitmap, DRO_FILL, (FIXED) 0x10000) ;
    GpiMove(hpsBitmap, &ptLastR) ;
    GpiFullArc(hpsBitmap, DRO_FILL, (FIXED) 0x10000) ;

さて、これで新しい位置に目玉が描画されました。 しかしこの描画はメモリPSについて行なわれたものですので、 まだ画面には反映されていません。 それでは最後に、メモリPSで更新されたイメージを シェイプウィンドウに反映させることにしましょう。

    /*
     * update shape window
     */
    
    WinSendMsg(hwndShape, SHAPEWIN_MSG_UPDATE, MPFROMP(&rctUpdt), NULL) ;

このメッセージによってシェイプウィンドウは 描画元のメモリPSの内容を取り込んで画面を更新します。 今までの内容をまとめると以下の関数が出来上がります。

---- drawEyes ここから ----
/*
 * drawEyes - draw eye balls on Bitmap (Memory PS)
 */

static  void    calcPos(PPOINTL base, PPOINTL ptr, PPOINTL eye)
{
    int     ix, iy, rad ;
    double  r ;
    
    ix = ptr->x - base->x ;
    iy = ptr->y - base->y ;
    rad = EYE_RO ;
    
    if (((ix *  ix) + (iy * iy)) <= (rad * rad)) {
        eye->x = ptr->x ;
	eye->y = ptr->y ;
    } else {
        r = atan2((double) ix, (double) iy) ;
	eye->x = base->x + (LONG) (sin(r) * (double) rad) ;
	eye->y = base->y + (LONG) (cos(r) * (double) rad) ;
    }
}

static  POINTL  ptLast  = { 0, 0 } ;
static  POINTL  ptCentL = { EYE_XL, EYE_Y } ;
static  POINTL  ptCentR = { EYE_XR, EYE_Y } ;
static  POINTL  ptLastL = { EYE_XL, EYE_Y } ;
static  POINTL  ptLastR = { EYE_XR, EYE_Y } ;
static  RECTL   rctUpdt = { RCT_XL, RCT_YB, RCT_XR, RCT_YT } ;

static  void    drawEyes(void)
{
    ARCPARAMS   arcParam ;
    POINTL      pt ;

    if (hwndFrame == NULLHANDLE || hwndShape == NULLHANDLE) {
        return ;
    }
    if (hpsBitmap == NULLHANDLE) {
        return ;
    }
    
    /*
     * get mouse position, do nothing if not changed
     */
     
    WinQueryPointerPos(HWND_DESKTOP, &pt) ;
    WinMapWindowPoints(HWND_DESKTOP, hwndFrame, &pt, 1) ;

    if (pt.x == ptLast.x && pt.y == ptLast.y) {
        return ;
    }
    ptLast.x = pt.x ;
    ptLast.y = pt.y ;
    
    /*
     * prepare arc params for eye balls
     */
     
    arcParam.lP = EYE_RI ;
    arcParam.lQ = EYE_RI ;
    arcParam.lR = 0 ;
    arcParam.lS = 0 ;
    GpiSetArcParams(hpsBitmap, &arcParam) ;
    
    /*
     * clear last position
     */

    GpiSetColor(hpsBitmap, CLR_BACK) ;
    GpiMove(hpsBitmap, &ptLastL) ;
    GpiFullArc(hpsBitmap, DRO_FILL, (FIXED) 0x10000) ;
    GpiMove(hpsBitmap, &ptLastR) ;
    GpiFullArc(hpsBitmap, DRO_FILL, (FIXED) 0x10000) ;

    /*
     * calc. new positions
     */
    
    calcPos(&ptCentL, &pt, &ptLastL) ;
    calcPos(&ptCentR, &pt, &ptLastR) ;

    /*
     * draw on new positions
     */
     
    GpiSetColor(hpsBitmap, CLR_BALL) ;
    GpiMove(hpsBitmap, &ptLastL) ;
    GpiFullArc(hpsBitmap, DRO_FILL, (FIXED) 0x10000) ;
    GpiMove(hpsBitmap, &ptLastR) ;
    GpiFullArc(hpsBitmap, DRO_FILL, (FIXED) 0x10000) ;

    /*
     * update shape window
     */
    
    WinSendMsg(hwndShape, SHAPEWIN_MSG_UPDATE, MPFROMP(&rctUpdt), NULL) ;
}
---- drawEyes ここまで ----