前面我们已经讲过键盘事件的处理,这次聊聊鼠标事件处理。我们从队列里获取到事件以后,可以检测事件的类型,鼠标事件有两个:鼠标键(按下、松开)和鼠标移动。一般的检测代码如下:
1 SDL_Event myEvent;//事件 2 int quit=0; 3 4 while (!quit) 5 { 6 while (SDL_PollEvent(&myEvent)) 7 { 8 switch (myEvent.type)//检测事件类型 9 { 10 case SDL_QUIT: 11 quit = 1; 12 break; 13 case SDL_MOUSEBUTTONDOWN://鼠标按下 14 //to do 15 break; 16 case SDL_MOUSEBUTTONUP://鼠标键松开 17 //to do 18 break; 19 case SDL_MOUSEMOTION://鼠标移动 20 //to do 21 break; 22 } 23 } 24 }
在SDL_MOUSEBUTTONDOWN、SDL_MOUSEBUTTONUP事件中,我们可以检测鼠标按键信息,然后加以处理。鼠标按键事件的结构:
typedef struct SDL_MouseButtonEvent { Uint8 type; /* SDL_MOUSEBUTTONDOWN or SDL_MOUSEBUTTONUP */ Uint8 which; /* The mouse device index */ Uint8 button; /* 鼠标按键,左、右、中三个键*/ Uint8 state; /* SDL_PRESSED按下 or SDL_RELEASED松开 */ Uint16 x, y; /* 鼠标按下时的坐标 */ } SDL_MouseButtonEvent;
鼠标移动事件的结构为:
typedef struct SDL_MouseMotionEvent { Uint8 type; /* SDL_MOUSEMOTION */ Uint8 which; /* The mouse device index */ Uint8 state; /* 鼠标状态 */ Uint16 x, y; /* 鼠标当前坐标 */ Sint16 xrel; /* 鼠标在x方向的位移 */ Sint16 yrel; /* 鼠标在y方向的位移*/ } SDL_MouseMotionEvent;
当检测出是那个鼠标事件后,我们可以根据事件结构采取相应的处理,下面以一个登录界面上两个按钮为例,说明如何处理鼠标事件。
界面如左图所示,有两个按钮登录和注册,每个按钮都有三种状态,鼠标不在按钮上是第一种状态,鼠标移动到按钮上是第二种状态,按下鼠标时第三种状态。其相应图片做成了一个精灵图(右图)。代码如下:
1 /* 2 功能:演示SDL鼠标事件 3 作者:csl 4 日期:2012-5-14 5 */ 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include "SDL.h" 9 10 typedef enum {LEFT,RIGHT}BUTTONPOS;//枚举按钮在精灵图中的编号 11 typedef enum {LOGIN,REGISTER,OTHER}BUTTONFLAG;//枚举按钮 12 13 //屏幕尺寸 14 #define SCREENWIDTH 432 15 #define SCREENHEIGH 287 16 #define BPP 32 17 18 //按钮尺寸 19 #define BUTTONWIDTH 81 20 #define BUTTONHEIGH 22 21 22 //定义各个按钮的位置 23 SDL_Rect position[2] = {{123,206,81,22},{215,206,81,22}}; 24 SDL_Surface *gpScreen = NULL;//显示表面 25 SDL_Surface *gpBackground = NULL;//背景 26 SDL_Surface *gpButton = NULL; 27 SDL_Event myEvent;//事件 28 29 30 SDL_Surface *loadImage(char *aFilename); 31 void cleanUp();//程序退出时清理内存 32 int isOnButton(int aX,int aY,int aIndex); 33 void changeButton(BUTTONPOS aPos,int aNo,SDL_Rect *aDst); 34 35 int main(int argc,char *argv[]) 36 { 37 int quit = 0; 38 int x,y; 39 SDL_Rect src,dst; 40 BUTTONFLAG selected = OTHER;//按钮选中标志 41 int current=-1; 42 43 if((SDL_Init(SDL_INIT_VIDEO)==-1)) //初始化视频子系统 44 { 45 printf("Unable to init SDL: %s\n", SDL_GetError()); 46 exit(-1); 47 } 48 atexit(cleanUp);// 注册cleanUp,当退出时调用,使得退出时程序自动清理 49 50 //创建32位窗口 51 gpScreen = SDL_SetVideoMode(SCREENWIDTH,SCREENHEIGH, BPP, SDL_HWSURFACE | SDL_HWPALETTE | SDL_DOUBLEBUF ); 52 if(!gpScreen) 53 { 54 exit(1); 55 } 56 57 //载入各个图片 58 gpBackground = loadImage("jpg\\register.jpg"); 59 gpButton = loadImage("jpg\\button.jpg"); 60 if (!gpBackground||!gpButton) 61 { 62 exit(0); 63 } 64 65 //显示背景图 66 SDL_BlitSurface(gpBackground,NULL,gpScreen,NULL); 67 SDL_Flip(gpScreen); 68 69 //事件处理 70 while (!quit) 71 { 72 while (SDL_PollEvent(&myEvent)) 73 { 74 switch (myEvent.type)//检测事件类型 75 { 76 case SDL_QUIT: 77 quit = 1; 78 break; 79 case SDL_MOUSEBUTTONDOWN://鼠标按下 80 switch(myEvent.button.button) 81 { 82 case SDL_BUTTON_LEFT: 83 x = myEvent.button.x;//得到当前鼠标的坐标 84 y = myEvent.button.y; 85 86 //判断鼠标是否落在登录按钮里 87 if (isOnButton(x,y,0)==1) 88 { 89 selected=LOGIN; 90 changeButton(LEFT,2,&position[0]); 91 SDL_Flip(gpScreen); 92 } 93 else if (isOnButton(x,y,1)==1)//判断鼠标是否落在注册按钮里 94 { 95 selected=REGISTER; 96 changeButton(RIGHT,2,&position[1]); 97 SDL_Flip(gpScreen); 98 } 99 break; 100 } 101 break; 102 case SDL_MOUSEBUTTONUP: 103 switch(myEvent.button.button) 104 { 105 case SDL_BUTTON_LEFT: 106 x = myEvent.button.x;//得到当前鼠标的坐标 107 y = myEvent.button.y; 108 109 //判断鼠标是否落在登录按钮里 110 if (selected==LOGIN) 111 { 112 changeButton(LEFT,1,&position[0]); 113 SDL_Flip(gpScreen); 114 } 115 else if (selected==REGISTER)//判断鼠标是否落在注册按钮里 116 { 117 changeButton(RIGHT,1,&position[1]); 118 SDL_Flip(gpScreen); 119 } 120 selected = OTHER; 121 break; 122 } 123 break; 124 case SDL_MOUSEMOTION://鼠标移动 125 x = myEvent.button.x;//得到当前鼠标的坐标 126 y = myEvent.button.y; 127 128 //判断鼠标是否落在登录按钮里 129 if (isOnButton(x,y,0)) 130 { 131 changeButton(LEFT,1,&position[0]); 132 changeButton(RIGHT,0,&position[1]); 133 SDL_Flip(gpScreen); 134 } 135 else if(isOnButton(x,y,1)) 136 { 137 changeButton(LEFT,0,&position[0]); 138 changeButton(RIGHT,1,&position[1]); 139 SDL_Flip(gpScreen); 140 } 141 else 142 { 143 changeButton(LEFT,0,&position[0]); 144 changeButton(RIGHT,0,&position[1]); 145 SDL_Flip(gpScreen); 146 } 147 break; 148 } 149 } 150 } 151 152 system("pause"); 153 return 0; 154 } 155 156 157 /*-------------------------------------------------------------------- 158 函数名: loadImage 159 参 数: char *filename 图像文件的名字 160 返回值: SDL_Surface * 返回指向图像表面的指针 161 功 能: 载入图像 162 备 注: 163 ----------------------------------------------------------------------*/ 164 SDL_Surface *loadImage(char *aFilename) 165 { 166 SDL_Surface* loadedImage = NULL; 167 SDL_Surface* optimizedImage = NULL; 168 169 //载入图像 170 loadedImage = IMG_Load( aFilename); 171 172 if( NULL != loadedImage )//If the image loaded 173 { 174 //创建优化图像 175 optimizedImage = SDL_DisplayFormat( loadedImage ); 176 177 //释放loadImage 178 SDL_FreeSurface( loadedImage ); 179 } 180 return optimizedImage; 181 } 182 183 /*-------------------------------------------------------------------- 184 函数名: cleanUp 185 参 数: 无 186 返回值: 无 187 功 能: 程序退出时,清理内存 188 备 注: 189 ----------------------------------------------------------------------*/ 190 void cleanUp() 191 { 192 if (gpBackground) 193 { 194 SDL_FreeSurface(gpBackground); 195 } 196 if (gpButton) 197 { 198 SDL_FreeSurface(gpButton); 199 } 200 201 SDL_FreeSurface(gpScreen); 202 SDL_Quit(); 203 } 204 205 /*-------------------------------------------------------------------- 206 函数名: isOnButton 207 参 数: (x,y)鼠标当前坐标; 208 index要判断的那个按钮,0表示登录按钮,1表示注册按钮 209 返回值: 如果落在指定按钮上返回1,否则返回0 210 功 能: 判断鼠标是否落在指定按钮上面 211 备 注: 212 ----------------------------------------------------------------------*/ 213 int isOnButton(int aX,int aY, int aIndex) 214 { 215 return aX>=position[aIndex].x && aX<=(position[aIndex].x+position[aIndex].w)&& 216 aY>=position[aIndex].y && aY<=(position[aIndex].y + position[aIndex].h); 217 } 218 219 /*-------------------------------------------------------------------- 220 函数名: changeButton 221 参 数: aPos表示要取精灵图左边还是右边的按钮; 222 aNO要取的那个按钮,从上往下依次为0,1,2 223 返回值: 无 224 功 能: 将指定按钮表面传输到显示表面 225 备 注: 226 ----------------------------------------------------------------------*/ 227 void changeButton(BUTTONPOS aPos,int aNo,SDL_Rect *aDst) 228 { 229 SDL_Rect src; 230 231 switch(aPos) 232 { 233 case LEFT: 234 src.x = 0; 235 break; 236 case RIGHT: 237 src.x = BUTTONWIDTH + 1; 238 break; 239 } 240 src.y =aNo? (aNo * BUTTONHEIGH + 1):(aNo * BUTTONHEIGH); 241 src.w = BUTTONWIDTH; 242 src.h = BUTTONHEIGH; 243 SDL_BlitSurface(gpButton,&src,gpScreen,aDst); 244 }
程序中定义了两个枚举类型,第一个BUTTONPOS,用于表示从精灵如上提取图片,LEFT表示左边那一列,RIGHT表示右边那一列。position数组保存了登录和注册按钮在显示表面上的位置;第二个枚举表示是否选中指定按钮,LOGIN表示选中登录、REGISTER表示选中注册、OTHER表示未选中。
第74行检测事件类型,第79行检测是否是鼠标按下事件,如果是,则要检测按下的是那个键,如果是左键按下,要将更换按钮图片,检测代码是:
switch(myEvent.button.button) { case SDL_BUTTON_LEFT: x = myEvent.button.x;//得到当前鼠标的坐标 y = myEvent.button.y; //判断鼠标是否落在登录按钮里 if (isOnButton(x,y,0)==1) { selected=LOGIN; changeButton(LEFT,2,&position[0]); SDL_Flip(gpScreen); } else if (isOnButton(x,y,1)==1)//判断鼠标是否落在注册按钮里 { selected=REGISTER; changeButton(RIGHT,2,&position[1]); SDL_Flip(gpScreen); } break; }
switch的判断表达myEvent.button.button用于检测是那个鼠标按键,如果是左键,首先得到鼠标坐标,然后调用isOnButton判断是否落在指定按钮上,如果落在按钮上,首先将选中标志selected置值,然后调用changeButton函数修改按钮显示的图片。
判断鼠标是否落在按钮上的函数isOnButton有三个参数,前两个是鼠标坐标,第三个是按钮编号,0是登录,1是注册,只要判断鼠标的坐标(x,y)落在按钮区域内就可以。
改变按钮图片,思路比较简单,比如说当按下登录按钮时我们需要把精灵图中左边的第3个按钮显示出来,所以传LEFT,2和显示的位置,在函数里首先计算左边第3个按钮的位置,主要是计算第3个按钮的左上角坐标,宽和高是固定的,计算完了就进行位块传输的指定位置。
鼠标按键松开和鼠标移动处理情况类似。这里不过多讲述了,需要源代码请点击这儿下载。