天天看點

【音視訊連載-003】基礎學習篇-SDL 消息循環和事件響應SDL 消息循環和事件響應鍵盤響應滑鼠響應自定義事件響應總結

作者:星隕

來源:

音視訊開發進階

在前面的文章中已經建立了一個 SDL 視窗并且顯示指定的顔色。

為了讓視窗顯示出來,在程式中寫了一個死循環,這幾行代碼就是 SDL 消息循環和事件響應的核心縮影了。

SDL_Event windowEvent;
    while (true){
        if (SDL_PollEvent(&windowEvent)){
            if (SDL_QUIT == windowEvent.type){
                break;
            }
        }
    }           

SDL 消息循環和事件響應

和 Android 中的 Handler 機制有些類似,Handler 會關聯一個線程,線程内部維護一個消息隊列 MessageQueue,通過 Handler 像 MessageQueue 發送消息,然後再從 MessageQueue 中取出 Message 進行處理。

在 SDL 中通過

SDL_PollEvent

從消息隊列取出消息,如果有則傳回 1,沒用則傳回 0。

SDL_Event

結構體代表消息事件,其中的

type

指定具體的事件類型,在

SDL_events.h

檔案中能看到所有的事件類型,抄錄一些比較典型的:

typedef enum
{
   /* Application events */
    SDL_QUIT           = 0x100, /**< User-requested quit */

   /* Keyboard events */
    SDL_KEYDOWN        = 0x300, /**< Key pressed */
    SDL_KEYUP,                  /**< Key released */

    /* Mouse events */
    SDL_MOUSEMOTION    = 0x400, /**< Mouse moved */
    SDL_MOUSEBUTTONDOWN,        /**< Mouse button pressed */
    SDL_MOUSEBUTTONUP,          /**< Mouse button released */
    SDL_MOUSEWHEEL,             /**< Mouse wheel motion */

    /* Window events */
    SDL_WINDOWEVENT    = 0x200, /**< Window state change */
    SDL_SYSWMEVENT,             /**< System specific event */
} SDL_EventType;           

如上所示,有鍵盤、滑鼠事件還有視窗事件和應用退出的事件,基本上也就用到這些了。

當使用者點選了視窗左上角

的時候,對應 SDL_Event 的 type 就是

SDL_QUIT

,這個 type 是一定要添加處理的,不然點叉就關閉不了視窗了。

bool bQuit = false;
    while (!bQuit){
        while (SDL_PollEvent(&windowEvent)){
            switch (windowEvent.type){
                case SDL_QUIT:
                    bQuit = true;
                    break;
                default:
                    break;
            }
        }
    }           

除了

SDL_PollEvent

方法去取消息外,還有

SDL_WaitEvent

方法。顧名思義,該方法會阻塞目前調用的線程,直到取出一個消息為止。

bool bQuit = false;
    while (!bQuit){
        SDL_WaitEvent(&windowEvent);
        if (windowEvent.type == SDL_QUIT){
            bQuit = true;
            break;
        } else{
            cout << "get event" << endl;
        }
    }           

使用方法如上所示,但實際上程式不會一直卡在

SDL_WaitEvent

上,因為它沒有限制監聽的事件類型,是以隻要有視窗在運作顯示,哪怕你滑鼠在視窗上滑過、或者按下了鍵盤,都能算是收到了消息事件,

cout

方法列印的 log 日志會不斷出現的。

同樣的,在

SDL_WaitEvent

方法中監聽了

SDL_QUIT

類型的事件,當點選視窗左上角的叉時,也要退出循環,結束程式。

鍵盤響應

現在可以通過

SDL_Event

的事件類型來監聽特定的鍵盤事件了。

鍵盤事件有

SDL_KEYDOWN

按下和

SDL_KEYUP

擡起兩種類型,按需監聽。

而具體使用者點選鍵盤上什麼按鍵,這個資訊就在 SDL_Event 的

SDL_KeyboardEvent

中。

對于不同類型的事件所包含的具體資訊,SDL_Event 都有對應的結構體去存儲。

typedef union SDL_Event
{
    Uint32 type;                    /**< Event type, shared with all events */
    SDL_CommonEvent common;         /**< Common event data */
    SDL_DisplayEvent display;       /**< Window event data */
    SDL_WindowEvent window;         /**< Window event data */
    // 鍵盤事件的資訊
    SDL_KeyboardEvent key;          /**< Keyboard event data */
    // 滑鼠事件的資訊
    SDL_MouseButtonEvent button;    /**< Mouse button event data */
    SDL_MouseMotionEvent motion;    /**< Mouse motion event data */
}           

是以想要知道使用者點選了哪個按鍵,去找

SDL_KeyboardEvent

對應的資訊就好了。

bool bQuit = false;
    while (!bQuit){
        while (SDL_PollEvent(&windowEvent)){
            switch (windowEvent.type){
                case SDL_QUIT:
                    bQuit = true;
                    break;
                case SDL_KEYDOWN:
                    if (windowEvent.key.keysym.sym == SDLK_SPACE){
                        cout << "user click space \n" ;
                    }
                    break;
                default:
                    break;
            }
        }
    }           

以上代碼監聽使用者是否點選空格鍵,如果是就輸出對應的 log 。

滑鼠響應

除此之外還可以監聽滑鼠事件,比如滑鼠是否按下、擡起、移動和坐标之類的。

對應的事件類型是

SDL_MOUSEMOTION

SDL_MOUSEBUTTONDOWN

SDL_MOUSEBUTTONUP

SDL_MOUSEBUTTONUP

,按自己的需求去監聽了。

事件包含的具體資訊在

SDL_MouseMotionEvent

SDL_MouseButtonEvent

SDL_MouseWheelEvent

裡面。

bool bQuit = false;
    while (!bQuit){
        while (SDL_PollEvent(&windowEvent)){
            switch (windowEvent.type){
                case SDL_QUIT:
                    bQuit = true;
                    break;
                case SDL_MOUSEBUTTONDOWN:
                    printf("button index  is %d\n",windowEvent.button.button);
                    break;
                default:
                    break;
            }
        }
    }           

以上代碼就是監聽滑鼠點選事件,并且列印出點選按鍵的 index ,滑鼠的左鍵、右鍵和中間滾輪按下去對應的 index 不同。

自定義事件響應

除了系統事件,還可以自定義事件。

首先定義一個事件類型的宏:

#define SDL_CUSTOM_EVENT  (SDL_USEREVENT + 1)           

其次,要建立一個線程,讓它延時五秒後,發送自定義事件,在主線程中去接收到這個事件。

// 線程運作函數
int sdl_thread_custom_event(void *){
    // 延時 5 秒
    SDL_Delay(5000);
    // 建立自定義事件并發送到消息隊列中去
    SDL_Event sdlEvent;
    sdlEvent.type = SDL_CUSTOM_EVENT;
    SDL_PushEvent(&sdlEvent);
}
// 建立線程并運作
SDL_CreateThread(sdl_thread_custom_event, "custom_event", nullptr);           

線程運作函數如上所示,定義一個 SDL_Event ,把它的 type 指派為自定義的類型,然後通過

SDL_PushEvent

方法把該消息事件放到消息隊列中去。

bool bQuit = false;
    while (!bQuit){
        while (SDL_PollEvent(&windowEvent)){
            switch (windowEvent.type){
                case SDL_QUIT:
                    bQuit = true;
                    break;
                case SDL_CUSTOM_EVENT:
                    cout << "receive user custom event\n";
                    break;
                default:
                    break;
            }
        }
    }           

SDL_PollEvent 方法會從消息隊列中取到我們自定義的消息事件,這時候就能做一些想要的操作呢,比如列印 log 之類的。

總結

以上就是關于 SDL 消息循環和事件響應的學習連載

003

篇。基本上後續所有的 SDL 代碼都會有這樣一個消息循環作為程式的主架構,是以這個時候弄明白了,方面後面代碼的學習。

具體的代碼見倉庫:

https://github.com/glumes/av-beginner

本篇文章對應的送出

tag

av-beginner-003

,可切換至對應源碼檢視。

能力有限,文中有不對之處,歡迎加我微信 ezglumes 進行交流~~

SDL 系列文章
「視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。
【音視訊連載-003】基礎學習篇-SDL 消息循環和事件響應SDL 消息循環和事件響應鍵盤響應滑鼠響應自定義事件響應總結

繼續閱讀