SDLで入力制御

今回はマウスとキーボードを使ったサンプルプルグラムです。



キーを入力すると画面に描画する色を変更、

左のマウスボタンをクリックすると、画面に矩形を描画します。



イベントが発生したときのみ処理を実行するようにしています。SDLでのマウスを押した時のイベントは、SDL_MOUSEBUTTONDOWN。キーボードを押した時のイベントは、SDL_KEYDOWNになります。




ただしSDL_KEYDOWNに関しては、デフォルトの設定だと

キーリピートが有効ではありません。



キーボードを押したときに走るイベントが、

「キーを押す」→「キーを離す」の一連動作で実行されるため、SDLのプログラムを実行中にキーを押しっぱなしにした状態で、連続したキーボードイベントを発生させる場合、

SDL_EnableKeyRepeat
  (SDL_DEFAULT_REPEAT_DELAY,SDL_DEFAULT_REPEAT_INTERVAL);



を予め実行しておく必要があります。



今回は画面を更新する際に、画面に描画された箇所のみ更新するようにしています。

前回のプログラムでは描画した箇所に関わらず、全画面を更新していました。

つまり更新する必要の無いところまで更新していたのです。



その為、今回は複数の画面上の更新箇所を保存できるように、リストを使ってます。

ちょっとややこしいので、難しいと思ったら軽く眺めておいてください。


rectnode.h

#ifndef RECTNODE_H
#define RECTNODE_H

#include <SDL/SDL.h>

typedef struct RectHandle {
    void    (*add)(SDL_Rect *rect);
    void    (*update)(SDL_Surface *screen);
    void    (*free)(void);
}RectHandle;

void initRectHandle(RectHandle *rHnadle);

#endif


rectnode.c

#include "rectnode.h"
#include <stdlib.h>

//--矩形リスト構造体
typedef struct RECT_NODE {
    SDL_Rect rect;
    struct RECT_NODE *next;
} RectNode;

static RectNode *nodeTop = NULL;
static Uint16   numRect = 0;

//--関数宣言
static void     add(SDL_Rect *rect);
static void     update(SDL_Surface *screen);
static void     freeList(void);

//--関数ポインタ等の初期化
void initRectHandle(RectHandle *rHandle)
{
    rHandle->add    = add;
    rHandle->free   = freeList;
    rHandle->update = update;
}

//--矩形情報の追加
static void add(SDL_Rect *rect)
{
    RectNode **tmp;
    RectNode *node = (RectNode*)malloc(sizeof(RectNode));

    if(node == NULL)
    {
        return;
    }

    node->rect = (*rect);
    node->next = NULL;

    tmp = &nodeTop;
    while( *tmp != NULL )
    {
        tmp = &(*tmp)->next;
    }
    *tmp = node;
    
    //--矩形情報の追加
    numRect++;
}

//--画面の更新
static void update(SDL_Surface *screen)
{
    int idx;
    RectNode **tmp;
    SDL_Rect *rects = (SDL_Rect*)malloc(sizeof(SDL_Rect)*numRect);

    if(nodeTop == NULL)
    {
        return;
    }

    idx = 0;
    tmp = &nodeTop;
    while( *tmp != NULL )
    {
        rects[idx].h = (*tmp)->rect.h;
        rects[idx].w = (*tmp)->rect.w;
        rects[idx].x = (*tmp)->rect.x;
        rects[idx].y = (*tmp)->rect.y;

        idx++;
        tmp = &(*tmp)->next;
    }

    SDL_UpdateRects(screen, numRect, rects);

    free(rects);
}

//--矩形情報の削除
static void freeList(void)
{
    RectNode **tmp,*next;

    tmp = &nodeTop;
    while(*tmp != NULL)
    {
        next = (*tmp)->next;    //リストを退避

        free(*tmp);             //要素を解放
        
        *tmp = next;            //次のリスト
    }

    numRect = 0;
}






今回のメインとなるソースです。

キーリピートは予め有効にしてあります。

キーボードを押しっぱなしにしていると、左上に表示される色が刻々と変化します。

キーリピートのインターバルはデフォルト値で設定されています。

ミリ秒単位からの調節が可能ですので、興味のある方はソースをいじってみてください。


main.cpp

#include <SDL/SDL.h>
#include <stdio.h>
#include "rectnode.h"

#pragma comment(lib,"SDL.lib")
#pragma comment(lib,"SDLmain.lib")

#define WIDTH    320        //横幅
#define HEIGHT  256         //高さ
#define BPP     32          //1ピクセルのビット数
#define SCREEN_BLOCK 32
#define FLG_INIT    (SDL_INIT_VIDEO)

#define KEY_REPEAT  1       //キーリピートフラグ

static SDL_Surface *screen; //ビデオポインタ
static RectHandle handle;   //SDLコントロール変数
static Uint32 color;

bool init(void);            //初期化
void run(void);             //メイン処理
void quit(void);            //終了処理
void mouseAction(void);     //マウス釦押下イベント
//キーボードイベント
void keyboardAction(SDL_keysym *keysym);

int main(int argc, char *argv[])
{    
    if( init() )run();
    quit();
    
    return 0;
}

void mouseAction(void)
{
    int x,y;
    Uint8 flg;

    flg = SDL_GetMouseState( &x, &y );
    if( flg & SDL_BUTTON(1) )
    {
        SDL_Rect rect = {
            (x / SCREEN_BLOCK)*SCREEN_BLOCK,
            (y / SCREEN_BLOCK)*SCREEN_BLOCK,
            SCREEN_BLOCK,
            SCREEN_BLOCK
        };

        SDL_FillRect( screen, &rect, color );
        handle.add( &rect );    //矩形の追加
    }
}

void keyboardAction(SDL_keysym *keysym)
{
    Sint16 r,g,b;
    Uint16 random;
    int keyCode;

    r = (color>>16)&255;
    g = (color>>8)&255;
    b = color&255;

    keyCode = keysym->sym;
    random = (Uint16)rand();

    switch( random%3 ){
    case 0:     r = (~keyCode + random | 128)   & 255;  break;
    case 1:     g = (~keyCode + random | 128)   & 255;  break;
    case 2:     b = (~keyCode + random | 128)   & 255;  break;
    default:    break;
    }

    color = (r<<16)+(g<<8)+b;
}

bool init(void)
{
    //    ライブラリを初期化する
    if(SDL_Init(FLG_INIT) < 0) {
        printf("初期化に失敗");
        return false;
    }
    //    ビデオモードを設定する
    screen = SDL_SetVideoMode(WIDTH,HEIGHT,
                        BPP, SDL_SWSURFACE );
    if( screen == NULL) {
        printf("VideoInitialize Error");
        return false;
    }

    initRectHandle( &handle );

#if KEY_REPEAT != 0
    SDL_EnableKeyRepeat
        (SDL_DEFAULT_REPEAT_DELAY,
         SDL_DEFAULT_REPEAT_INTERVAL);
#endif
    return true;
}

void run(void)
{
    SDL_Event ev;
    bool flg;
    
    flg = false;
    //    次のイベントが来るまで無限に待機
    while( SDL_WaitEvent(&ev) )
    {
        switch(ev.type){
        case SDL_QUIT:        //ウィンドウの×ボタン
            printf("QUIT\n");
            return;
        case SDL_KEYDOWN:    //キーダウン
            printf("KeyDonw\n");
            keyboardAction( &(ev.key.keysym) );
            flg = true;
            break;
        case SDL_MOUSEBUTTONDOWN://マウスダウン
            printf("MouseDown\n");
            mouseAction();
            flg = true;
            break;
        default:break;        //それ以外のイベントは無視する
        }

        if( flg )
        {
            SDL_Rect rect = { 0,0,SCREEN_BLOCK, SCREEN_BLOCK };
            SDL_FillRect(screen, &rect, color);
            handle.add( &rect );    //矩形の追加

            //画面の更新
            handle.update(screen);
            handle.free();
            flg = false;
        }
    }
}

void quit(void)
{
    if(screen != NULL)
    {
        SDL_FreeSurface( screen );
    }
    SDL_Quit();
}