#define WINVER 0x0500                                  /* バージョン定義 Windows2000以上 */
#define _WIN32_WINNT WINVER
#undef DIRECTINPUT_VERSION
#define DIRECTINPUT_VERSION        (0x0800)            /* DirectInputバージョン定義 */

#include "ruby.h"
#ifndef RUBY_ST_H
#include "st.h"
#endif
#include <dinput.h>

#define DXRUBY_EXTERN 1
#include "dxruby.h"
#include "input.h"

#define PADMAX 2

#define P_LEFT     0
#define P_RIGHT    1
#define P_UP       2
#define P_DOWN     3
#define P_BUTTON0  4
#define P_BUTTON1  5
#define P_BUTTON2  6
#define P_BUTTON3  7
#define P_BUTTON4  8
#define P_BUTTON5  9
#define P_BUTTON6  10
#define P_BUTTON7  11
#define P_BUTTON8  12
#define P_BUTTON9  13
#define P_BUTTON10 14
#define P_BUTTON11 15
#define P_BUTTON12 16
#define P_BUTTON13 17
#define P_BUTTON14 18
#define P_BUTTON15 19
#define M_LBUTTON   0
#define M_RBUTTON   1
#define M_MBUTTON   2

static VALUE mInput;        /* インプットモジュール */
static LPDIRECTINPUT8        g_pDInput            = NULL; /* DirectInput */
static LPDIRECTINPUTDEVICE8  g_pDIDKeyBoard       = NULL; /* DirectInputのキーボードデバイス */
static LPDIRECTINPUTDEVICE8  g_pDIDJoyPad[PADMAX];        /* DirectInputのパッドデバイス     */
static BYTE g_diKeyState[256];                            /* DirectInputでのキーボード入力用バッファ */
static BYTE g_diKeyStateOld[256];                         /* DirectInputでのキーボード入力用バッファ１フレーム前 */
static BYTE g_diKeyCount[256];                            /* DirectInputでのキーボード入力用カウンタ */
static BYTE g_diKeyConfig[256];                           /* DirectInputでのキーボード・パッド割り当て */
static BYTE g_diKeyWait[256];                             /* オートリピートウェイト時間 */
static BYTE g_diKeyInterval[256];                         /* オートリピート間隔 */
static int g_JoystickCount = 0;
static BYTE g_byMouseState_L, g_byMouseStateOld_L;
static BYTE g_byMouseState_M, g_byMouseStateOld_M;
static BYTE g_byMouseState_R, g_byMouseStateOld_R;

/* Pad情報 */
static struct DXRubyPadInfo {
    int right;
    int left;
    int up;
    int down;
    LPDIRECTINPUTDEVICE8  pDIDJoyPad; /* DirectInputのパッドデバイス */
} g_PadInfo[PADMAX];

/* Pad状態 */
static struct DXRubyPadState {
    char button[20];
    int PadConfig[20];
    int count[20];
    int wait[20];
    int interval[20];
} g_PadState[PADMAX], g_PadStateOld[PADMAX];

/*********************************************************************
 * Inputモジュール
 *
 * DirectInputを使用してキーボード・パッドの入力を行う。
 *********************************************************************/

/*--------------------------------------------------------------------
   Windowsメッセージ処理込みの入力更新
 ---------------------------------------------------------------------*/
VALUE Input_update( VALUE obj )
{
    MSG msg;
    msg.message = WM_NULL;

    if( g_WindowInfo.userloop == 2 )
    {
        rb_raise( eDXRubyError, "Window.loop実行中にInput.updateは実行できません。" );
    }

    g_WindowInfo.userloop = 1;

    /* メッセージループ */
    /* メッセージがなくなるまで処理をし続ける */
    /* メッセージが無い時はブロックをyieldする */
    while (1)
    {
        if( PeekMessage( &msg, 0, 0, 0, PM_REMOVE ) != 0)
        {
            /* メッセージがある時 */
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
        else
        {
            break;
        }
    }

    if( g_WindowInfo.requestclose == 1 )
    {
        g_WindowInfo.requestclose = 0;
        return Qtrue;
    }

    /* メッセージが無い時 */
    /* 入力状態更新 */
    inputupdate_internal();

    return Qfalse;
}


/*--------------------------------------------------------------------
   Inputモジュールの入力処理
 ---------------------------------------------------------------------*/
void inputupdate_internal( void )
{
    int i, j;
    DIJOYSTATE paddata;

    /* フォーカスがはずれた場合、キーとパッド、マウスボタンの入力を受け付けなくする */
    if( g_WindowInfo.active == 0 )
    {
        /* キーボードのデータをクリアする */
        ZeroMemory( &g_diKeyState, sizeof(g_diKeyState) );
        ZeroMemory( &g_diKeyStateOld, sizeof(g_diKeyStateOld) );

        /* パッドデータをクリアする */
        for( i = 0; i < g_JoystickCount; i++ )
        {
            for( j = 0; j < 20; j++ )
            {
                g_PadStateOld[i].button[j] = g_PadState[i].button[j];
                g_PadState[i].button[j] = 0;
            }
        }

        /* マウスボタンのデータをクリアする */
        g_byMouseStateOld_L = g_byMouseState_L;
        g_byMouseStateOld_M = g_byMouseState_M;
        g_byMouseStateOld_R = g_byMouseState_R;
        g_byMouseState_L = 0;
        g_byMouseState_M = 0;
        g_byMouseState_R = 0;

        return;
    }

    /* デバイスアクセス権を再取得する */
    g_pDIDKeyBoard->lpVtbl->Acquire( g_pDIDKeyBoard );

    for( i = 0; i < g_JoystickCount; i++ )
    {
        g_pDIDJoyPad[i]->lpVtbl->Poll( g_pDIDJoyPad[i] );
        g_pDIDJoyPad[i]->lpVtbl->Acquire( g_pDIDJoyPad[i] );
    }

    /* キーボードの直接データを取得する */
    memcpy( g_diKeyStateOld, g_diKeyState, sizeof(g_diKeyState) );
    g_pDIDKeyBoard->lpVtbl->GetDeviceState( g_pDIDKeyBoard, 256, g_diKeyState );

    /* ゲームパッドのデータを取得する */
    for( i = 0; i < g_JoystickCount; i++ )
    {
        int j;

        g_pDIDJoyPad[i]->lpVtbl->GetDeviceState( g_pDIDJoyPad[i], sizeof(DIJOYSTATE), &paddata );

        for( j = 0; j < 20; j++ )
        {
            g_PadStateOld[i].button[j] = g_PadState[i].button[j];
            g_PadState[i].button[j] = 0;
        }

        /* 左 */
        if( paddata.lX < g_PadInfo[i].left )
        {
            g_PadState[i].button[P_LEFT] = 1;
        }
        /* 右 */
        else if( paddata.lX > g_PadInfo[i].right )
        {
            g_PadState[i].button[P_RIGHT] = 1;
        }

        /* 上 */
        if( paddata.lY < g_PadInfo[i].up )
        {
            g_PadState[i].button[P_UP] = 1;
        }
        /* 下 */
        else if( paddata.lY > g_PadInfo[i].down )
        {
            g_PadState[i].button[P_DOWN] = 1;
        }

        /* ボタン状態 */
        for( j = 0; j < 16; j++)
        {
            g_PadState[i].button[j + 4] = paddata.rgbButtons[j] >> 7;
        }
    }

    /* マウスボタンの状態 */
    g_byMouseStateOld_L = g_byMouseState_L;
    g_byMouseStateOld_M = g_byMouseState_M;
    g_byMouseStateOld_R = g_byMouseState_R;
    g_byMouseState_L = GetKeyState( VK_LBUTTON );
    g_byMouseState_M = GetKeyState( VK_MBUTTON );
    g_byMouseState_R = GetKeyState( VK_RBUTTON );

    return;
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   横方向の入力をxの増分で返す
 ---------------------------------------------------------------------*/
static VALUE Input_x( int argc, VALUE *argv, VALUE obj )
{
    int x = 0;
	VALUE vnumber;
	int number;

    rb_scan_args( argc, argv, "01", &vnumber);

	number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));

	if( number < 0 || number >= PADMAX )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_x" );
    }

	/* ひだり */
    if( g_diKeyState[g_PadState[number].PadConfig[P_LEFT]] & 0x80  || g_PadState[number].button[P_LEFT] == 1 )
    {
        x = x - 1;
    }

    /* みぎ */
    if( g_diKeyState[g_PadState[number].PadConfig[P_RIGHT]] & 0x80  || g_PadState[number].button[P_RIGHT] == 1 )
    {
        x = x + 1;
    }

    return INT2FIX( x );
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   縦方向の入力をyの増分で返す
 ---------------------------------------------------------------------*/
static VALUE Input_y( int argc, VALUE *argv, VALUE obj )
{
    int y = 0;
	VALUE vnumber;
	int number;

    rb_scan_args( argc, argv, "01", &vnumber);

	number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));

	if( number < 0 || number >= PADMAX )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_y" );
    }


    /* うえ */
    if( g_diKeyState[g_PadState[number].PadConfig[P_UP]] & 0x80  || g_PadState[number].button[P_UP] == 1 )
    {
        y = y - 1;
    }

    /* した */
    if( g_diKeyState[g_PadState[number].PadConfig[P_DOWN]] & 0x80  || g_PadState[number].button[P_DOWN] == 1 )
    {
        y = y + 1;
    }

    return INT2FIX( y );
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押されていたらtrueになる。引数はキーコード。
 ---------------------------------------------------------------------*/
static VALUE Input_keyDown( VALUE obj , VALUE vkey )
{
    int key, padbutton = 0;

    key = NUM2INT( vkey );
    if( key < 0 || key >= 256 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_keyDown" );
    }

    if( g_diKeyConfig[key] != -1 && g_PadState[g_diKeyConfig[key] / 20].button[g_diKeyConfig[key] % 20] == 1 )
    {
        padbutton = 1;
    }
    
    if( g_diKeyState[key] & 0x80 ||  padbutton == 1)
    {
        return Qtrue;
    }

    return Qfalse;
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押した瞬間だけtrueになる。引数はキーコード。
 ---------------------------------------------------------------------*/
static VALUE Input_keyPush( VALUE obj , VALUE vkey )
{
    int key, padbutton = 0, padbuttonold = 0;

    key = NUM2INT( vkey );
    if( key < 0 || key >= 256 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_keyPush" );
    }

    if( g_diKeyConfig[key] != -1 )
    {
        if( g_PadState[g_diKeyConfig[key] / 20].button[g_diKeyConfig[key] % 20] == 1 )
        {
            padbutton = 1;
        }
        if( g_PadStateOld[g_diKeyConfig[key] / 20].button[g_diKeyConfig[key] % 20] == 1 )
        {
            padbuttonold = 1;
        }
    }

    if( (g_diKeyState[key] & 0x80) ||  padbutton == 1 ) /* 入力されている */
    {
        if( !(g_diKeyStateOld[key] & 0x80) && padbuttonold == 0 )    /* 前回OFFである */
        {
            g_diKeyCount[key] = 0;                  /* カウント初期化 */
            return Qtrue;                           /* なにはともあれTrue */
        }
        else                                    /* 前回ONだった */
        {
            g_diKeyCount[key]++;                    /* カウント */
            if( g_diKeyWait[key] <= g_diKeyCount[key] ) /* ウェイトタイムを超えたか */
            {
                if( g_diKeyInterval[key] != 0 &&
                    (g_diKeyCount[key] - g_diKeyWait[key]) % g_diKeyInterval[key] == 0 ) /* インターバルタイムごとに */
                {
                    return Qtrue;                           /* True */
                }
            }
        }
    }

    return Qfalse;
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押されていたらtrueになる。引数はボタン番号。
 ---------------------------------------------------------------------*/
static VALUE Input_padDown( int argc, VALUE *argv, VALUE obj )
{
	VALUE vnumber, vbutton;
	int number, button, key = 0;

    rb_scan_args( argc, argv, "11", &vbutton, &vnumber);

	button = NUM2INT( vbutton );
	number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));

	if( number < 0 || button < 0 || number >= PADMAX || button >= 20 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_padDown" );
    }

    if( g_PadState[number].PadConfig[button] != -1 && g_diKeyState[g_PadState[number].PadConfig[button]] & 0x80 )
    {
        key = 1;
    }

    if( key == 1 || g_PadState[number].button[button] == 1 )
    {
        return Qtrue;
    }

    return Qfalse;
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押した瞬間だけtrueになる。引数はボタン番号。
 ---------------------------------------------------------------------*/
static VALUE Input_padPush( int argc, VALUE *argv, VALUE obj )
{
	VALUE vnumber, vbutton;
	int number, button, key = 0, keyold = 0;

    rb_scan_args( argc, argv, "11", &vbutton, &vnumber);

	button = NUM2INT( vbutton );
	number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));

	if( number < 0 || button < 0 || number >= PADMAX || button >= 20 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_padPush" );
    }

    if( g_PadState[number].PadConfig[button] != -1 )
    {
        if( g_diKeyState[g_PadState[number].PadConfig[button]] & 0x80 )
        {
            key = 1;
        }
        if( g_diKeyStateOld[g_PadState[number].PadConfig[button]] & 0x80 )
        {
            keyold = 1;
        }
    }

    if( key == 1 || g_PadState[number].button[button] == 1 ) /* 入力されている */
    {
        if( keyold == 0 && g_PadStateOld[number].button[button] == 0 ) /* 前回OFFである */
        {
            g_PadState[number].count[button] = 0;                           /* カウント初期化 */
            return Qtrue;                                                   /* なにはともあれTrue */
        }
        else                                                           /* 前回ONだった */
        {
            g_PadState[number].count[button]++;                             /* カウント */
            if( g_PadState[number].wait[button] <= g_PadState[number].count[button] ) /* ウェイトタイムを超えたか */
            {
                if( g_PadState[number].interval[button] != 0 &&
                   (g_PadState[number].count[button] - g_PadState[number].wait[button])
                    % g_PadState[number].interval[button] == 0 )   /* インターバルタイムごとに */
                {
                    return Qtrue;                                                           /* True */
                }
            }
        }
    }

    return Qfalse;
}


/*--------------------------------------------------------------------
   パッドとキーの対応を設定する
 ---------------------------------------------------------------------*/
static void Input_SetConfig( int number, int pad, int key )
{
    int i, j;

    if( pad == -1 )    /* キーに対するパッド割り当て解除処理 */
    {
        g_diKeyConfig[key] = -1;
        for( i = 0; i < PADMAX; i++)
        {
            for( j = 0; j < 20; j++)
            {
                if( g_PadState[i].PadConfig[j] == key )
                {
                    g_PadState[i].PadConfig[j] = -1;
                }
            }
        }
    }
    else if( key == -1 || key == 0 ) /* パッドに対するキー割り当て解除処理 */
    {
        g_PadState[number].PadConfig[pad] = -1;
        for( i = 0; i < 256; i++)
        {
            if( g_diKeyConfig[i] == pad )
            {
                g_diKeyConfig[i] = -1;
            }
        }
    }
    else    /* 割り当て処理 */
    {
        g_PadState[number].PadConfig[pad] = key;
        g_diKeyConfig[key] = number * 20 + pad;
    }
}

static VALUE Input_setconfig( int argc, VALUE *argv, VALUE obj )
{
    VALUE vnumber, vpad, vkey;
    int number, pad, key;

    rb_scan_args( argc, argv, "21", &vpad, &vkey, &vnumber);

    number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));
    pad = (vpad == Qnil ? -1 : NUM2INT( vpad ));
    key = (vkey == Qnil ? -1 : NUM2INT( vkey ));

    if( number < 0 || pad < -1 || key < -1 || number >= PADMAX || pad >= 20 || key >= 256 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_setconfig" );
    }

    if( pad != -1 || key != -1 )
    {
        Input_SetConfig( number, pad, key );
    }

    return obj;
}


/*--------------------------------------------------------------------
   マウスホイールの状態を返す
 ---------------------------------------------------------------------*/
static VALUE Input_getmousewheelpos( VALUE obj )
{
    return INT2FIX(g_WindowInfo.mousewheelpos);
}


/*--------------------------------------------------------------------
   マウスホイールの状態を設定する
 ---------------------------------------------------------------------*/
static VALUE Input_setmousewheelpos( VALUE obj, VALUE wheelpos )
{
    g_WindowInfo.mousewheelpos = NUM2INT( wheelpos );

    return obj;
}


/*--------------------------------------------------------------------
   マウスのボタン状態を返す
 ---------------------------------------------------------------------*/
static VALUE Input_mouseDown( VALUE obj, VALUE button )
{

    switch( NUM2INT( button ) )
    {
    case M_LBUTTON:
        if( g_byMouseState_L & 0x80 )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }

    case M_RBUTTON:
        if( g_byMouseState_R & 0x80 )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }

    case M_MBUTTON:
        if( g_byMouseState_M & 0x80 )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }
    }

    return Qnil;
}


/*--------------------------------------------------------------------
   マウスのボタン状態を返す
 ---------------------------------------------------------------------*/
static VALUE Input_mousePush( VALUE obj, VALUE button )
{

    switch( NUM2INT( button ) )
    {
    case M_LBUTTON:
        if( (g_byMouseState_L & 0x80) && !(g_byMouseStateOld_L & 0x80) )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }

    case M_RBUTTON:
        if( g_byMouseState_R & 0x80 && !(g_byMouseStateOld_R & 0x80) )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }

    case M_MBUTTON:
        if( g_byMouseState_M & 0x80 && !(g_byMouseStateOld_M & 0x80) )
        {
            return Qtrue;
        }
        else
        {
            return Qfalse;
        }
    }

    return Qnil;
}


/*--------------------------------------------------------------------
   マウスカーソルの位置を返す
 ---------------------------------------------------------------------*/
static VALUE Input_getmouseposx( VALUE obj )
{
    POINT cursor;

    GetCursorPos( &cursor );
    ScreenToClient( g_hWnd, &cursor );
    return INT2FIX( cursor.x );
}


/*--------------------------------------------------------------------
   マウスカーソルの位置を返す
 ---------------------------------------------------------------------*/
static VALUE Input_getmouseposy( VALUE obj )
{
    POINT cursor;

    GetCursorPos( &cursor );
    ScreenToClient( g_hWnd, &cursor );

    return INT2FIX( cursor.y );
}


/*--------------------------------------------------------------------
   マウスカーソルの位置を設定する
 ---------------------------------------------------------------------*/
static VALUE Input_setmousepos( VALUE klass, VALUE vx, VALUE vy )
{
    POINT cursor;

    cursor.x = NUM2INT( vx );
    cursor.y = NUM2INT( vy );
    ClientToScreen( g_hWnd, &cursor );
    SetCursorPos( cursor.x, cursor.y );

    return Qnil;
}


/*--------------------------------------------------------------------
   マウス描画するかどうかの設定
 ---------------------------------------------------------------------*/
static VALUE Input_enablemouse( VALUE obj, VALUE draw )
{
    VALUE flag = Qfalse;
    int c;

    if( draw != Qnil && draw != Qfalse )
    {
        flag = Qtrue;
    }

    if( flag == Qtrue )
    {
        c = ShowCursor( TRUE );
        while( c < 0 ) c = ShowCursor( TRUE );
    }
    else
    {
        c = ShowCursor( FALSE );
        while( c >= 0 ) c = ShowCursor( FALSE );
    }

    g_WindowInfo.enablemouse = flag;

    return draw;
}


/*--------------------------------------------------------------------
   Inputモジュールの全キーオートリピート状態設定
 ---------------------------------------------------------------------*/
static VALUE Input_setrepeat( VALUE obj , VALUE vwait, VALUE vinterval )
{
    int wait, interval, i, j;

    wait = NUM2INT( vwait );
    interval = NUM2INT( vinterval );

    for( i = 0; i < 256; i++)
    {
        g_diKeyWait[i] = wait;
        g_diKeyInterval[i] = interval;
    }

    for( i = 0; i < PADMAX; i++)
    {
        for( j = 0; j < 20; j++)
        {
            g_PadState[i].wait[j] = wait;
            g_PadState[i].interval[j] = interval;
        }
    }

    return obj;
}


/*--------------------------------------------------------------------
   Inputモジュールのキーオートリピート状態設定
 ---------------------------------------------------------------------*/
static VALUE Input_setkeyrepeat( VALUE obj , VALUE vkey, VALUE vwait, VALUE vinterval )
{
    int key, wait, interval;

    key = NUM2INT( vkey );
    wait = NUM2INT( vwait );
    interval = NUM2INT( vinterval );

    if( key < 0 || key >= 256 || wait < 0 || interval < 0 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_setKeyRepeat" );
    }

    g_diKeyWait[key] = wait;
    g_diKeyInterval[key] = interval;

    return obj;
}


/*--------------------------------------------------------------------
   Inputモジュールのパッドオートリピート状態設定
 ---------------------------------------------------------------------*/
static VALUE Input_setpadrepeat( int argc, VALUE *argv, VALUE obj )
{
    VALUE vpad, vwait, vinterval, vnumber;
    int pad, wait, interval, number;

    rb_scan_args( argc, argv, "31", &vpad, &vwait, &vinterval, &vnumber );

    pad = NUM2INT( vpad );
    wait = NUM2INT( vwait );
    interval = NUM2INT( vinterval );
    number = (vnumber == Qnil ? 0 : NUM2INT( vnumber ));

    if( number < 0 || pad < 0 || number >= PADMAX || pad >= 20 ||
        wait < 0 || interval < 0 )
    {
        rb_raise( eDXRubyError, "値が範囲外です。 - Input_setPadRepeat" );
    }

    g_PadState[number].wait[pad] = wait;
    g_PadState[number].interval[pad] = interval;

    return obj;
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押されているキーの配列を返す。
 ---------------------------------------------------------------------*/
static VALUE Input_getKeys( VALUE obj )
{
    int i, j;
    VALUE buf[256];

    for( i = 0, j = 0; i < 256; i++ )
    {
        if( g_diKeyState[i] & 0x80 ) /* 入力されている */
        {
            buf[j++] = INT2FIX( i );
        }
    }

    return rb_ary_new4( j, buf );
}


/*--------------------------------------------------------------------
   Inputモジュールのデータ取得

   押されているパッドの配列を返す。
 ---------------------------------------------------------------------*/
static VALUE Input_getPads( int argc, VALUE *argv, VALUE obj )
{
    int i, j, number;
    VALUE buf[20];
    VALUE vnumber;

    rb_scan_args( argc, argv, "01", &vnumber);

    number = vnumber == Qnil ? 0 : NUM2INT( vnumber );

    for( i = 0, j = 0; i < 20; i++ )
    {
        if( g_PadState[number].button[i] == 1 )
        {
            buf[j++] = INT2FIX( i );
        }
    }

    return rb_ary_new4( j, buf );
}


/*--------------------------------------------------------------------
   パッドの数を返す
 ---------------------------------------------------------------------*/
static VALUE Input_getPadNum( VALUE obj )
{
    return INT2FIX( g_JoystickCount );
}


/* デバイス列挙関数 */
BOOL CALLBACK EnumJoysticksCallback( const DIDEVICEINSTANCE* pdidInstance, void* pContext )
{
    HRESULT hr;

    /* 列挙されたジョイスティックへのインターフェイスを取得する */
    hr = g_pDInput->lpVtbl->CreateDevice( g_pDInput,
                            &pdidInstance->guidInstance,
                            &g_pDIDJoyPad[g_JoystickCount],
                            NULL );
    if( SUCCEEDED( hr ) )
    {
        return (++g_JoystickCount == PADMAX)?(DIENUM_STOP):(DIENUM_CONTINUE);
    }

    return DIENUM_STOP;
}

/* オブジェクト（軸）列挙関数 */
BOOL CALLBACK EnumAxisCallback( const DIDEVICEOBJECTINSTANCE* pdidoi, void *pContext )
{
    HRESULT hr;
    DIPROPRANGE diprg = { sizeof(DIPROPRANGE), sizeof(DIPROPHEADER) };
    struct DXRubyPadInfo *pad;

    pad = (struct DXRubyPadInfo *)pContext;

    diprg.diph.dwHow = DIPH_BYID; 
    diprg.diph.dwObj = pdidoi->dwType;

    /*========================================================*/
    /* 取得 */
    hr =  pad->pDIDJoyPad->lpVtbl->GetProperty( pad->pDIDJoyPad, DIPROP_RANGE, &diprg.diph );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - GetProperty" );
    }

    /* x軸 */
    if( memcmp( &pdidoi->guidType, &GUID_XAxis ,sizeof(GUID)) == 0 )
    {
        int xcenter;
        xcenter = (diprg.lMin + diprg.lMax) / 2;
        pad->left  = (xcenter - diprg.lMin) / 2;
        pad->right = (diprg.lMax - xcenter) / 2 + xcenter;
    }

    /* y軸 */
    if( memcmp( &pdidoi->guidType, &GUID_YAxis ,sizeof(GUID)) == 0 )
    {
        int ycenter;
        ycenter = (diprg.lMin + diprg.lMax) / 2;
        pad->up   = (ycenter - diprg.lMin) / 2;
        pad->down = (diprg.lMax - ycenter) / 2 + ycenter;
    }

    return DIENUM_CONTINUE;
}

/*--------------------------------------------------------------------
  （内部関数）DirectInput初期化
 ---------------------------------------------------------------------*/
static void InitDirectInput( void )
{
    HRESULT hr;
    int i;

/* DirectInput初期化 */

    /* DirectInputオブジェクトの作成 */
    hr = DirectInput8Create( g_hInstance, DIRECTINPUT_VERSION,
                             &IID_IDirectInput8, (void **)&g_pDInput, NULL );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - DirectInput8Create" );
    }

/* キーボード */

    /* デバイス・オブジェクトを作成（キーボード） */
    hr = g_pDInput->lpVtbl->CreateDevice( g_pDInput, &GUID_SysKeyboard, &g_pDIDKeyBoard, NULL );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - CreateDevice" );
    }

    /* データ形式を設定（キーボードですよ） */
    hr = g_pDIDKeyBoard->lpVtbl->SetDataFormat( g_pDIDKeyBoard, &c_dfDIKeyboard );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - SetDataFormat" );
    }

    /* キーボードのモードを設定（フォアグラウンド＆非排他モード） */
    hr = g_pDIDKeyBoard->lpVtbl->SetCooperativeLevel( g_pDIDKeyBoard, g_hWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - SetCooperativeLevel" );
    }

    /* 入力制御開始 */
    g_pDIDKeyBoard->lpVtbl->Acquire( g_pDIDKeyBoard );


/* ゲームパッド */

    for( i = 0; i < PADMAX; i++)
    {
        g_pDIDJoyPad[i] = NULL;
    }
    g_JoystickCount = 0;

    /* ゲームデバイス列挙 */
    hr = g_pDInput->lpVtbl->EnumDevices( g_pDInput, DI8DEVCLASS_GAMECTRL, EnumJoysticksCallback, NULL, DIEDFL_ATTACHEDONLY );

    if( FAILED( hr ) )
    {
        rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - EnumDevices" );
    }

    /* ゲームデバイス初期化 */
    for ( i = 0; i < g_JoystickCount; i++ )
    {
        /* データ形式を設定 */
        hr = g_pDIDJoyPad[i]->lpVtbl->SetDataFormat( g_pDIDJoyPad[i], &c_dfDIJoystick );

        if( FAILED( hr ) )
        {
            rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - SetDataFormat" );
        }

        /* キーボードのモードを設定（フォアグラウンド＆非排他モード） */
        hr = g_pDIDJoyPad[i]->lpVtbl->SetCooperativeLevel( g_pDIDJoyPad[i], g_hWnd, DISCL_NONEXCLUSIVE | DISCL_FOREGROUND );

        if( FAILED( hr ) )
        {
            rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - SetCooperativeLevel" );
        }

        /* オブジェクト列挙 */
        g_PadInfo[i].pDIDJoyPad = g_pDIDJoyPad[i];
        hr = g_pDIDJoyPad[i]->lpVtbl->EnumObjects( g_pDIDJoyPad[i], EnumAxisCallback, &g_PadInfo[i], DIDFT_AXIS );

        if( FAILED( hr ) )
        {
            rb_raise( eDXRubyError, "DirectInputの初期化に失敗しました - EnumObjects" );
        }

        /* 入力制御開始 */
        g_pDIDJoyPad[i]->lpVtbl->Acquire( g_pDIDJoyPad[i] );
    }

}


void Input_release( void )
{
    int i;

    for( i = 0 ; i < g_JoystickCount; i++ )
    {
        /* DirectInputDevice(JoyPad)オブジェクトの使用終了 */
        if( g_pDIDJoyPad[i] )
        {
            g_pDIDJoyPad[i]->lpVtbl->Unacquire( g_pDIDJoyPad[i] ); 
        }

        /* DirectInputDevide(JoyPad)オブジェクトの破棄 */
        RELEASE( g_pDIDJoyPad[i] );
    }

    /* DirectInputDevice(Keyboard)オブジェクトの使用終了 */
    if( g_pDIDKeyBoard )
    {
           g_pDIDKeyBoard->lpVtbl->Unacquire( g_pDIDKeyBoard ); 
    }

    /* DirectInputDevide(Keyboard)オブジェクトの破棄 */
    RELEASE( g_pDIDKeyBoard );

    /* DirectInputオブジェクトの破棄 */
    RELEASE( g_pDInput );
}

void Init_dxruby_Input( void )
{
    int i, j;

    /* Inputモジュール登録 */
    mInput = rb_define_module_under( mDXRuby, "Input" );

    /* Inputモジュールにメソッド登録 */
    rb_define_singleton_method( mInput, "x"              , Input_x               , -1 );
    rb_define_singleton_method( mInput, "y"              , Input_y               , -1 );
    rb_define_singleton_method( mInput, "set_mouse_pos"  , Input_setmousepos     , 2  );
    rb_define_singleton_method( mInput, "setMousePos"    , Input_setmousepos     , 2  );
    rb_define_singleton_method( mInput, "mouse_pos_x"      , Input_getmouseposx    , 0  );
    rb_define_singleton_method( mInput, "mousePosX"      , Input_getmouseposx    , 0  );
    rb_define_singleton_method( mInput, "mouse_pos_y"      , Input_getmouseposy    , 0  );
    rb_define_singleton_method( mInput, "mousePosY"      , Input_getmouseposy    , 0  );
    rb_define_singleton_method( mInput, "key_down?"       , Input_keyDown         , 1  );
    rb_define_singleton_method( mInput, "keyDown?"       , Input_keyDown         , 1  );
    rb_define_singleton_method( mInput, "key_push?"       , Input_keyPush         , 1  );
    rb_define_singleton_method( mInput, "keyPush?"       , Input_keyPush         , 1  );
    rb_define_singleton_method( mInput, "pad_down?"       , Input_padDown         , -1 );
    rb_define_singleton_method( mInput, "padDown?"       , Input_padDown         , -1 );
    rb_define_singleton_method( mInput, "pad_push?"       , Input_padPush         , -1 );
    rb_define_singleton_method( mInput, "padPush?"       , Input_padPush         , -1 );
    rb_define_singleton_method( mInput, "mouse_down?"     , Input_mouseDown       , 1  );
    rb_define_singleton_method( mInput, "mouseDown?"     , Input_mouseDown       , 1  );
    rb_define_singleton_method( mInput, "mouse_push?"     , Input_mousePush       , 1  );
    rb_define_singleton_method( mInput, "mousePush?"     , Input_mousePush       , 1  );
    rb_define_singleton_method( mInput, "mouse_enable="   , Input_enablemouse     , 1  );
    rb_define_singleton_method( mInput, "mouseEnable="   , Input_enablemouse     , 1  );
    rb_define_singleton_method( mInput, "mouse_wheel_pos"  , Input_getmousewheelpos, 0  );
    rb_define_singleton_method( mInput, "mouseWheelPos"  , Input_getmousewheelpos, 0  );
    rb_define_singleton_method( mInput, "mouse_wheel_pos=" , Input_setmousewheelpos, 1  );
    rb_define_singleton_method( mInput, "mouseWheelPos=" , Input_setmousewheelpos, 1  );
    rb_define_singleton_method( mInput, "set_config"      , Input_setconfig       , -1 );
    rb_define_singleton_method( mInput, "setConfig"      , Input_setconfig       , -1 );
    rb_define_singleton_method( mInput, "set_repeat"      , Input_setrepeat       , 2  );
    rb_define_singleton_method( mInput, "setRepeat"      , Input_setrepeat       , 2  );
    rb_define_singleton_method( mInput, "update"         , Input_update          , 0  );
    rb_define_singleton_method( mInput, "set_key_repeat"   , Input_setkeyrepeat    , 3  );
    rb_define_singleton_method( mInput, "setKeyRepeat"   , Input_setkeyrepeat    , 3  );
    rb_define_singleton_method( mInput, "set_pad_repeat"   , Input_setpadrepeat    , -1 );
    rb_define_singleton_method( mInput, "setPadRepeat"   , Input_setpadrepeat    , -1 );
    rb_define_singleton_method( mInput, "keys"           , Input_getKeys         , 0  );
    rb_define_singleton_method( mInput, "pads"           , Input_getPads         , -1 );
    rb_define_singleton_method( mInput, "pad_num"        , Input_getPadNum       , 0 );

    /* キーボードのスキャンコード定数設定 */
    rb_define_const( mDXRuby, "K_ESCAPE"     , INT2FIX(DIK_ESCAPE) );
    rb_define_const( mDXRuby, "K_1"          , INT2FIX(DIK_1) );
    rb_define_const( mDXRuby, "K_2"          , INT2FIX(DIK_2) );
    rb_define_const( mDXRuby, "K_3"          , INT2FIX(DIK_3) );
    rb_define_const( mDXRuby, "K_4"          , INT2FIX(DIK_4) );
    rb_define_const( mDXRuby, "K_5"          , INT2FIX(DIK_5) );
    rb_define_const( mDXRuby, "K_6"          , INT2FIX(DIK_6) );
    rb_define_const( mDXRuby, "K_7"          , INT2FIX(DIK_7) );
    rb_define_const( mDXRuby, "K_8"          , INT2FIX(DIK_8) );
    rb_define_const( mDXRuby, "K_9"          , INT2FIX(DIK_9) );
    rb_define_const( mDXRuby, "K_0"          , INT2FIX(DIK_0) );
    rb_define_const( mDXRuby, "K_MINUS"      , INT2FIX(DIK_MINUS) );
    rb_define_const( mDXRuby, "K_EQUALS"     , INT2FIX(DIK_EQUALS) );
    rb_define_const( mDXRuby, "K_BACK"       , INT2FIX(DIK_BACK) );
    rb_define_const( mDXRuby, "K_TAB"        , INT2FIX(DIK_TAB) );
    rb_define_const( mDXRuby, "K_Q"          , INT2FIX(DIK_Q) );
    rb_define_const( mDXRuby, "K_W"          , INT2FIX(DIK_W) );
    rb_define_const( mDXRuby, "K_E"          , INT2FIX(DIK_E) );
    rb_define_const( mDXRuby, "K_R"          , INT2FIX(DIK_R) );
    rb_define_const( mDXRuby, "K_T"          , INT2FIX(DIK_T) );
    rb_define_const( mDXRuby, "K_Y"          , INT2FIX(DIK_Y) );
    rb_define_const( mDXRuby, "K_U"          , INT2FIX(DIK_U) );
    rb_define_const( mDXRuby, "K_I"          , INT2FIX(DIK_I) );
    rb_define_const( mDXRuby, "K_O"          , INT2FIX(DIK_O) );
    rb_define_const( mDXRuby, "K_P"          , INT2FIX(DIK_P) );
    rb_define_const( mDXRuby, "K_LBRACKET"   , INT2FIX(DIK_LBRACKET) );
    rb_define_const( mDXRuby, "K_RBRACKET"   , INT2FIX(DIK_RBRACKET) );
    rb_define_const( mDXRuby, "K_RETURN"     , INT2FIX(DIK_RETURN) );
    rb_define_const( mDXRuby, "K_LCONTROL"   , INT2FIX(DIK_LCONTROL) );
    rb_define_const( mDXRuby, "K_A"          , INT2FIX(DIK_A) );
    rb_define_const( mDXRuby, "K_S"          , INT2FIX(DIK_S) );
    rb_define_const( mDXRuby, "K_D"          , INT2FIX(DIK_D) );
    rb_define_const( mDXRuby, "K_F"          , INT2FIX(DIK_F) );
    rb_define_const( mDXRuby, "K_G"          , INT2FIX(DIK_G) );
    rb_define_const( mDXRuby, "K_H"          , INT2FIX(DIK_H) );
    rb_define_const( mDXRuby, "K_J"          , INT2FIX(DIK_J) );
    rb_define_const( mDXRuby, "K_K"          , INT2FIX(DIK_K) );
    rb_define_const( mDXRuby, "K_L"          , INT2FIX(DIK_L) );
    rb_define_const( mDXRuby, "K_SEMICOLON"  , INT2FIX(DIK_SEMICOLON) );
    rb_define_const( mDXRuby, "K_APOSTROPHE" , INT2FIX(DIK_APOSTROPHE) );
    rb_define_const( mDXRuby, "K_GRAVE"      , INT2FIX(DIK_GRAVE) );
    rb_define_const( mDXRuby, "K_LSHIFT"     , INT2FIX(DIK_LSHIFT) );
    rb_define_const( mDXRuby, "K_BACKSLASH"  , INT2FIX(DIK_BACKSLASH) );
    rb_define_const( mDXRuby, "K_Z"          , INT2FIX(DIK_Z) );
    rb_define_const( mDXRuby, "K_X"          , INT2FIX(DIK_X) );
    rb_define_const( mDXRuby, "K_C"          , INT2FIX(DIK_C) );
    rb_define_const( mDXRuby, "K_V"          , INT2FIX(DIK_V) );
    rb_define_const( mDXRuby, "K_B"          , INT2FIX(DIK_B) );
    rb_define_const( mDXRuby, "K_N"          , INT2FIX(DIK_N) );
    rb_define_const( mDXRuby, "K_M"          , INT2FIX(DIK_M) );
    rb_define_const( mDXRuby, "K_COMMA"      , INT2FIX(DIK_COMMA) );
    rb_define_const( mDXRuby, "K_PERIOD"     , INT2FIX(DIK_PERIOD) );
    rb_define_const( mDXRuby, "K_SLASH"      , INT2FIX(DIK_SLASH) );
    rb_define_const( mDXRuby, "K_RSHIFT"     , INT2FIX(DIK_RSHIFT) );
    rb_define_const( mDXRuby, "K_MULTIPLY"   , INT2FIX(DIK_MULTIPLY) );
    rb_define_const( mDXRuby, "K_LMENU"      , INT2FIX(DIK_LMENU) );
    rb_define_const( mDXRuby, "K_SPACE"      , INT2FIX(DIK_SPACE) );
    rb_define_const( mDXRuby, "K_CAPITAL"    , INT2FIX(DIK_CAPITAL) );
    rb_define_const( mDXRuby, "K_F1"         , INT2FIX(DIK_F1) );
    rb_define_const( mDXRuby, "K_F2"         , INT2FIX(DIK_F2) );
    rb_define_const( mDXRuby, "K_F3"         , INT2FIX(DIK_F3) );
    rb_define_const( mDXRuby, "K_F4"         , INT2FIX(DIK_F4) );
    rb_define_const( mDXRuby, "K_F5"         , INT2FIX(DIK_F5) );
    rb_define_const( mDXRuby, "K_F6"         , INT2FIX(DIK_F6) );
    rb_define_const( mDXRuby, "K_F7"         , INT2FIX(DIK_F7) );
    rb_define_const( mDXRuby, "K_F8"         , INT2FIX(DIK_F8) );
    rb_define_const( mDXRuby, "K_F9"         , INT2FIX(DIK_F9) );
    rb_define_const( mDXRuby, "K_F10"        , INT2FIX(DIK_F10) );
    rb_define_const( mDXRuby, "K_NUMLOCK"    , INT2FIX(DIK_NUMLOCK) );
    rb_define_const( mDXRuby, "K_SCROLL"     , INT2FIX(DIK_SCROLL) );
    rb_define_const( mDXRuby, "K_NUMPAD7"    , INT2FIX(DIK_NUMPAD7) );
    rb_define_const( mDXRuby, "K_NUMPAD8"    , INT2FIX(DIK_NUMPAD8) );
    rb_define_const( mDXRuby, "K_NUMPAD9"    , INT2FIX(DIK_NUMPAD9) );
    rb_define_const( mDXRuby, "K_SUBTRACT"   , INT2FIX(DIK_SUBTRACT) );
    rb_define_const( mDXRuby, "K_NUMPAD4"    , INT2FIX(DIK_NUMPAD4) );
    rb_define_const( mDXRuby, "K_NUMPAD5"    , INT2FIX(DIK_NUMPAD5) );
    rb_define_const( mDXRuby, "K_NUMPAD6"    , INT2FIX(DIK_NUMPAD6) );
    rb_define_const( mDXRuby, "K_ADD"        , INT2FIX(DIK_ADD) );
    rb_define_const( mDXRuby, "K_NUMPAD1"    , INT2FIX(DIK_NUMPAD1) );
    rb_define_const( mDXRuby, "K_NUMPAD2"    , INT2FIX(DIK_NUMPAD2) );
    rb_define_const( mDXRuby, "K_NUMPAD3"    , INT2FIX(DIK_NUMPAD3) );
    rb_define_const( mDXRuby, "K_NUMPAD0"    , INT2FIX(DIK_NUMPAD0) );
    rb_define_const( mDXRuby, "K_DECIMAL"    , INT2FIX(DIK_DECIMAL) );
    rb_define_const( mDXRuby, "K_OEM_102"    , INT2FIX(DIK_OEM_102) );
    rb_define_const( mDXRuby, "K_F11"        , INT2FIX(DIK_F11) );
    rb_define_const( mDXRuby, "K_F12"        , INT2FIX(DIK_F12) );
    rb_define_const( mDXRuby, "K_F13"        , INT2FIX(DIK_F13) );
    rb_define_const( mDXRuby, "K_F14"        , INT2FIX(DIK_F14) );
    rb_define_const( mDXRuby, "K_F15"        , INT2FIX(DIK_F15) );
    rb_define_const( mDXRuby, "K_KANA"       , INT2FIX(DIK_KANA) );
    rb_define_const( mDXRuby, "K_ABNT_C1"    , INT2FIX(DIK_ABNT_C1) );
    rb_define_const( mDXRuby, "K_CONVERT"    , INT2FIX(DIK_CONVERT) );
    rb_define_const( mDXRuby, "K_NOCONVERT"  , INT2FIX(DIK_NOCONVERT) );
    rb_define_const( mDXRuby, "K_YEN"        , INT2FIX(DIK_YEN) );
    rb_define_const( mDXRuby, "K_ABNT_C2"    , INT2FIX(DIK_ABNT_C2) );
    rb_define_const( mDXRuby, "K_NUMPADEQUALS", INT2FIX(DIK_NUMPADEQUALS) );
    rb_define_const( mDXRuby, "K_PREVTRACK"  , INT2FIX(DIK_PREVTRACK) );
    rb_define_const( mDXRuby, "K_AT"         , INT2FIX(DIK_AT) );
    rb_define_const( mDXRuby, "K_COLON"      , INT2FIX(DIK_COLON) );
    rb_define_const( mDXRuby, "K_UNDERLINE"  , INT2FIX(DIK_UNDERLINE) );
    rb_define_const( mDXRuby, "K_KANJI"      , INT2FIX(DIK_KANJI) );
    rb_define_const( mDXRuby, "K_STOP"       , INT2FIX(DIK_STOP) );
    rb_define_const( mDXRuby, "K_AX"         , INT2FIX(DIK_AX) );
    rb_define_const( mDXRuby, "K_UNLABELED"  , INT2FIX(DIK_UNLABELED) );
    rb_define_const( mDXRuby, "K_NEXTTRACK"  , INT2FIX(DIK_NEXTTRACK) );
    rb_define_const( mDXRuby, "K_NUMPADENTER", INT2FIX(DIK_NUMPADENTER) );
    rb_define_const( mDXRuby, "K_RCONTROL"   , INT2FIX(DIK_RCONTROL) );
    rb_define_const( mDXRuby, "K_MUTE"       , INT2FIX(DIK_MUTE) );
    rb_define_const( mDXRuby, "K_CALCULATOR" , INT2FIX(DIK_CALCULATOR) );
    rb_define_const( mDXRuby, "K_PLAYPAUSE"  , INT2FIX(DIK_PLAYPAUSE) );
    rb_define_const( mDXRuby, "K_MEDIASTOP"  , INT2FIX(DIK_MEDIASTOP) );
    rb_define_const( mDXRuby, "K_VOLUMEDOWN" , INT2FIX(DIK_VOLUMEDOWN) );
    rb_define_const( mDXRuby, "K_VOLUMEUP"   , INT2FIX(DIK_VOLUMEUP) );
    rb_define_const( mDXRuby, "K_WEBHOME"    , INT2FIX(DIK_WEBHOME) );
    rb_define_const( mDXRuby, "K_NUMPADCOMMA", INT2FIX(DIK_NUMPADCOMMA) );
    rb_define_const( mDXRuby, "K_DIVIDE"     , INT2FIX(DIK_DIVIDE) );
    rb_define_const( mDXRuby, "K_SYSRQ"      , INT2FIX(DIK_SYSRQ) );
    rb_define_const( mDXRuby, "K_RMENU"      , INT2FIX(DIK_RMENU) );
    rb_define_const( mDXRuby, "K_PAUSE"      , INT2FIX(DIK_PAUSE) );
    rb_define_const( mDXRuby, "K_HOME"       , INT2FIX(DIK_HOME) );
    rb_define_const( mDXRuby, "K_UP"         , INT2FIX(DIK_UP) );
    rb_define_const( mDXRuby, "K_PRIOR"      , INT2FIX(DIK_PRIOR) );
    rb_define_const( mDXRuby, "K_LEFT"       , INT2FIX(DIK_LEFT) );
    rb_define_const( mDXRuby, "K_RIGHT"      , INT2FIX(DIK_RIGHT) );
    rb_define_const( mDXRuby, "K_END"        , INT2FIX(DIK_END) );
    rb_define_const( mDXRuby, "K_DOWN"       , INT2FIX(DIK_DOWN) );
    rb_define_const( mDXRuby, "K_NEXT"       , INT2FIX(DIK_NEXT) );
    rb_define_const( mDXRuby, "K_INSERT"     , INT2FIX(DIK_INSERT) );
    rb_define_const( mDXRuby, "K_DELETE"     , INT2FIX(DIK_DELETE) );
    rb_define_const( mDXRuby, "K_LWIN"       , INT2FIX(DIK_LWIN) );
    rb_define_const( mDXRuby, "K_RWIN"       , INT2FIX(DIK_RWIN) );
    rb_define_const( mDXRuby, "K_APPS"       , INT2FIX(DIK_APPS) );
    rb_define_const( mDXRuby, "K_POWER"      , INT2FIX(DIK_POWER) );
    rb_define_const( mDXRuby, "K_SLEEP"      , INT2FIX(DIK_SLEEP) );
    rb_define_const( mDXRuby, "K_WAKE"       , INT2FIX(DIK_WAKE) );
    rb_define_const( mDXRuby, "K_WEBSEARCH"  , INT2FIX(DIK_WEBSEARCH) );
    rb_define_const( mDXRuby, "K_WEBFAVORITES", INT2FIX(DIK_WEBFAVORITES) );
    rb_define_const( mDXRuby, "K_WEBREFRESH" , INT2FIX(DIK_WEBREFRESH) );
    rb_define_const( mDXRuby, "K_WEBSTOP"    , INT2FIX(DIK_WEBSTOP) );
    rb_define_const( mDXRuby, "K_WEBFORWARD" , INT2FIX(DIK_WEBFORWARD) );
    rb_define_const( mDXRuby, "K_WEBBACK"    , INT2FIX(DIK_WEBBACK) );
    rb_define_const( mDXRuby, "K_MYCOMPUTER" , INT2FIX(DIK_MYCOMPUTER) );
    rb_define_const( mDXRuby, "K_MAIL"       , INT2FIX(DIK_MAIL) );
    rb_define_const( mDXRuby, "K_MEDIASELECT", INT2FIX(DIK_MEDIASELECT) );
    rb_define_const( mDXRuby, "K_BACKSPACE"  , INT2FIX(DIK_BACK) );
    rb_define_const( mDXRuby, "K_NUMPADSTAR" , INT2FIX(DIK_MULTIPLY) );
    rb_define_const( mDXRuby, "K_LALT"       , INT2FIX(DIK_LMENU) );
    rb_define_const( mDXRuby, "K_CAPSLOCK"   , INT2FIX(DIK_CAPITAL) );
    rb_define_const( mDXRuby, "K_NUMPADMINUS", INT2FIX(DIK_SUBTRACT) );
    rb_define_const( mDXRuby, "K_NUMPADPLUS" , INT2FIX(DIK_ADD) );
    rb_define_const( mDXRuby, "K_NUMPADPERIOD", INT2FIX(DIK_DECIMAL) );
    rb_define_const( mDXRuby, "K_NUMPADSLASH", INT2FIX(DIK_DIVIDE) );
    rb_define_const( mDXRuby, "K_RALT"       , INT2FIX(DIK_RMENU) );
    rb_define_const( mDXRuby, "K_UPARROW"    , INT2FIX(DIK_UP) );
    rb_define_const( mDXRuby, "K_PGUP"       , INT2FIX(DIK_PRIOR) );
    rb_define_const( mDXRuby, "K_LEFTARROW"  , INT2FIX(DIK_LEFT) );
    rb_define_const( mDXRuby, "K_RIGHTARROW" , INT2FIX(DIK_RIGHT) );
    rb_define_const( mDXRuby, "K_DOWNARROW"  , INT2FIX(DIK_DOWN) );
    rb_define_const( mDXRuby, "K_PGDN"       , INT2FIX(DIK_NEXT) );

    rb_define_const( mDXRuby, "P_UP"         , INT2FIX(P_UP)       );
    rb_define_const( mDXRuby, "P_LEFT"       , INT2FIX(P_LEFT)     );
    rb_define_const( mDXRuby, "P_RIGHT"      , INT2FIX(P_RIGHT)    );
    rb_define_const( mDXRuby, "P_DOWN"       , INT2FIX(P_DOWN)     );
    rb_define_const( mDXRuby, "P_BUTTON0"    , INT2FIX(P_BUTTON0)  );
    rb_define_const( mDXRuby, "P_BUTTON1"    , INT2FIX(P_BUTTON1)  );
    rb_define_const( mDXRuby, "P_BUTTON2"    , INT2FIX(P_BUTTON2)  );
    rb_define_const( mDXRuby, "P_BUTTON3"    , INT2FIX(P_BUTTON3)  );
    rb_define_const( mDXRuby, "P_BUTTON4"    , INT2FIX(P_BUTTON4)  );
    rb_define_const( mDXRuby, "P_BUTTON5"    , INT2FIX(P_BUTTON5)  );
    rb_define_const( mDXRuby, "P_BUTTON6"    , INT2FIX(P_BUTTON6)  );
    rb_define_const( mDXRuby, "P_BUTTON7"    , INT2FIX(P_BUTTON7)  );
    rb_define_const( mDXRuby, "P_BUTTON8"    , INT2FIX(P_BUTTON8)  );
    rb_define_const( mDXRuby, "P_BUTTON9"    , INT2FIX(P_BUTTON9)  );
    rb_define_const( mDXRuby, "P_BUTTON10"   , INT2FIX(P_BUTTON10) );
    rb_define_const( mDXRuby, "P_BUTTON11"   , INT2FIX(P_BUTTON11) );
    rb_define_const( mDXRuby, "P_BUTTON12"   , INT2FIX(P_BUTTON12) );
    rb_define_const( mDXRuby, "P_BUTTON13"   , INT2FIX(P_BUTTON13) );
    rb_define_const( mDXRuby, "P_BUTTON14"   , INT2FIX(P_BUTTON14) );
    rb_define_const( mDXRuby, "P_BUTTON15"   , INT2FIX(P_BUTTON15) );

    rb_define_const( mDXRuby, "M_LBUTTON"    , INT2FIX(M_LBUTTON)  );
    rb_define_const( mDXRuby, "M_RBUTTON"    , INT2FIX(M_RBUTTON)  );
    rb_define_const( mDXRuby, "M_MBUTTON"    , INT2FIX(M_MBUTTON)  );

    /* 割り当て初期化 */
    for( i = 0; i < 256; i++)
    {
        g_diKeyConfig[i] = -1;
    }
    for( i = 0; i < PADMAX; i++)
    {
        for( j = 0; j < 20; j++)
        {
            g_PadState[i].PadConfig[j] = -1;
        }
    }

    /* デフォルトの割り当て */
    Input_SetConfig( 0, P_LEFT   , DIK_LEFT  );
    Input_SetConfig( 0, P_RIGHT  , DIK_RIGHT );
    Input_SetConfig( 0, P_UP     , DIK_UP    );
    Input_SetConfig( 0, P_DOWN   , DIK_DOWN  );
    Input_SetConfig( 0, P_BUTTON0, DIK_Z     );
    Input_SetConfig( 0, P_BUTTON1, DIK_X     );
    Input_SetConfig( 0, P_BUTTON2, DIK_C     );

    /* DirectInput初期化 */
    InitDirectInput();

}


