最近 DR 在 Linux 上玩遊戲時,發現有不少遊戲在操控設定上無法識別除了基本三鍵以外的滑鼠按鍵,例如拇指旁的按鍵。但卻又不是每一款遊戲都有這問題,例如《惡靈勢力 2》(Left 4 Dead 2)就能夠識別到額外按鍵。這個狀況引起了 DR 的好奇心,基本上 Linux 遊戲的輸入控制都是利用 SDL 完成的,因此 DR 便想要自己動手做做看,並嘗試找出可能的差異。
一開始 DR 用以下的程式碼來識別滑鼠按鍵,但很快就發現了問題:
case SDL_MOUSEBUTTONDOWN:
switch (event.button.button) {
case SDL_BUTTON_LEFT:
printf("SDL_BUTTON_LEFT\n");
break;
case SDL_BUTTON_MIDDLE:
printf("SDL_BUTTON_MIDDLE\n");
break;
case SDL_BUTTON_RIGHT:
printf("SDL_BUTTON_RIGHT\n");
break;
case SDL_BUTTON_X1:
printf("SDL_BUTTON_X1\n");
break;
case SDL_BUTTON_X2:
printf("SDL_BUTTON_X2\n");
break;
}
break;
照理講 SDL_BUTTON_X1 和 SDL_BUTTON_X2 就是對應到額外的按鍵,但實際執行時並沒有產生作用(按了沒反應)。再者就算這兩個索引值有作用,總共也只有五個索引值可以用。以 DR 所使用的 Logitech MX518 為例,它一共有六個按鍵,比 SDL 所提供的索引值還多。
然而若是忽略索引值、直接顯示輸入事件(event)所回傳的數值,就會發現其實所有按鍵都是有反應的,而數值也都不一樣:
default:
printf("SDL_BUTTON: %d\n", event.button.button);
break;
也就是說,如果純粹用索引值來控制,就有可能發生只有基本三鍵有作用的狀況。反之,如果有考量到索引值以外的條件,那麼額外按鍵的識別就能夠做出來,最至少可以考慮全部定義為第四個按鍵。
以下是 DR 分別使用 SDL 1.2 及 2.0 版所撰寫的完整程式碼,其功能為建立一個空白視窗,然後在視窗中按下滑鼠按鍵就會輸出按鍵的數值。此外,這兩支程式碼也部份反應出 1.2 與 2.0 版的差異,例如視窗的建立方式、以及滑鼠滾輪的事件(event)類型。
sdl_mouse.c:
#include <stdio.h>
#include <SDL/SDL.h>
int main(int argc, char *argv[])
{
int quit = 0;
SDL_Surface* screen = NULL;
SDL_Event event;
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
exit(1);
}
screen = SDL_SetVideoMode(640, 480, 8, SDL_SWSURFACE);
if (screen == NULL) {
fprintf(stderr, "Couldn't set video mode: %s\n", SDL_GetError());
exit(1);
}
SDL_WM_SetCaption("SDL Mouse", NULL);
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 255, 255, 255));
SDL_Flip(screen);
while (quit == 0) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_MOUSEBUTTONDOWN:
switch (event.button.button) {
case SDL_BUTTON_LEFT:
printf("SDL_BUTTON_LEFT(%d)\n", event.button.button);
break;
case SDL_BUTTON_MIDDLE:
printf("SDL_BUTTON_MIDDLE(%d)\n", event.button.button);
break;
case SDL_BUTTON_RIGHT:
printf("SDL_BUTTON_RIGHT(%d)\n", event.button.button);
break;
case SDL_BUTTON_WHEELUP:
printf("SDL_BUTTON_WHEELUP(%d)\n", event.button.button);
break;
case SDL_BUTTON_WHEELDOWN:
printf("SDL_BUTTON_WHEELDOWN(%d)\n", event.button.button);
break;
default:
printf("SDL_BUTTON: %d\n", event.button.button);
break;
}
break;
case SDL_QUIT:
quit = 1;
break;
}
}
}
SDL_Quit();
return 0;
}
sdl2_mouse.c:
#include <stdio.h>
#include <SDL2/SDL.h>
int main(int argc, char *argv[])
{
int quit = 0;
SDL_Window* screen = NULL;
SDL_Surface* surface = NULL;
SDL_Event event;
if (SDL_Init(SDL_INIT_VIDEO) < 0) {
fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError());
exit(1);
}
screen = SDL_CreateWindow("SDL2 Mouse", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_SHOWN);
if (screen == NULL) {
fprintf(stderr, "Couldn't set video mode: %s\n", SDL_GetError());
exit(1);
}
surface = SDL_GetWindowSurface(screen);
SDL_FillRect(surface, NULL, SDL_MapRGB(surface->format, 255, 255, 255));
SDL_UpdateWindowSurface(screen);
while (quit == 0) {
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_MOUSEBUTTONDOWN:
switch (event.button.button) {
case SDL_BUTTON_LEFT:
printf("SDL_BUTTON_LEFT(%d)\n", event.button.button);
break;
case SDL_BUTTON_MIDDLE:
printf("SDL_BUTTON_MIDDLE(%d)\n", event.button.button);
break;
case SDL_BUTTON_RIGHT:
printf("SDL_BUTTON_RIGHT(%d)\n", event.button.button);
break;
default:
printf("SDL_BUTTON: %d\n", event.button.button);
break;
}
break;
case SDL_MOUSEWHEEL:
if (event.wheel.y == 1) {
printf("SDL_BUTTON_WHEELUP(%d)\n", event.wheel.y);
}
if (event.wheel.y == -1) {
printf("SDL_BUTTON_WHEELDOWN(%d)\n", event.wheel.y);
}
break;
case SDL_QUIT:
quit = 1;
break;
}
}
}
SDL_Quit();
return 0;
}
在 Fedora 19 上編譯需安裝 SDL 開發函式庫:
- sudo yum install SDL-devel SDL2-devel
而編譯指令如下:
- gcc -o sdl_mouse sdl_mouse.c `sdl-config --cflags --libs`
- gcc -o sdl2_mouse sdl2_mouse.c `sdl2-config --cflags --libs`