第六章—–键盘
windows程序中,对于键盘消息的处理,分为击键消息和字符消息。
击键消息
包括WM_KEYDOWM,WM_KEYUP,WM_SYSKEYDOWM,WM_SYSKEYUP,其中windows会自动处理WM_SYSKEYDOWM,WM_SYSKEYUP,应用程序则负责其他两种。
字符消息
也分为四种。
大多数情况下,我们只用处理非系统字符消息WM_CHAR就行。
综合示例之typer源程序。下面是一些核心代码。
宏定义:
#define BUFFER(x,y) *(pBuffer+y*cxBuffer+x) //表示光标所在处的内容
常量和一些变量定义
static DWORD dwCharSet=DEFAULT_CHARSET; //默认字体
static int cxChar,cyChar,cxClient,cyClient,cxBuffer,
cyBuffer,xCaret,yCaret;
static TCHAR *pBuffer=NULL;
HDC hdc;
int x,y,i;
PAINTSTRUCT ps;
TEXTMETRIC tm;
核心窗口处理过程:
case WM_CREATE:
hdc=GetDC(hwnd);
SelectObject(hdc,CreateFont(0,0,0,0,0,0,0,0,
dwCharSet,0,0,0,FIXED_PITCH,NULL));
GetTextMetrics(hdc,&tm);
cxChar=tm.tmAveCharWidth;
cyChar=tm.tmHeight;
DeleteObject(SelectObject(hdc,GetStockObject(SYSTEM_FONT)));
ReleaseDC(hwnd,hdc);
详解CreateFont函数,
nHeight,nWidth参数为0 ,表示的取默认高度和宽度,即SYSTEM _FONT。
同时由于该函数动态分配了内存,所以在获取字体属性不再使用后就释放掉,DeleteObject(SelectObject(hdc,GetStockObject(SYSTEM_FONT)))
case WM_SIZE: //发送WM_Paint的前期工作
if(message==WM_SIZE)
{
cxClient=LOWORD(lParam);
cyClient=HIWORD(lParam);
}
cxBuffer=max(1,cxClient/cxChar); //只要尺寸发生改变,就要更新一次cxClient,cyClient,cxBuffer,cyBuffer
cyBuffer=max(1,cyClient/cyChar);
if(pBuffer!=NULL) //同时释放之前的内容所占的内存空间,重新开辟
free(pBuffer);
pBuffer=(TCHAR*)malloc(cxBuffer*cyBuffer*sizeof(TCHAR));
for(y=0;y<cyBuffer;y++)
for(x=0;x<cxBuffer;x++)
BUFFER(x,y)=' ';
//设置光标为左上角
xCaret=0;
yCaret=0;
if(hwnd==GetFocus())
SetCaretPos(xCaret*cxChar,yCaret*cyChar);
InvalidateRect(hwnd,NULL,TRUE);
return 0;
为什么要在焦点集中窗口时,创建和显示插入符号,而不是即使一个程序中有多个窗口就同时创建多个插入符号?咋听着就很别扭了,的确。
第一,没必要多个窗口都闪烁着插入符号,插入符号的作用是提示用户焦点在该窗口。
第二,一个消息队列仅能够支持一个插入符号,如果你的应用程序有多个窗口,那么也只能共享一个插入符号。
case WM_SETFOCUS:
//创建和显示插入符号
CreateCaret(hwnd,NULL,cxChar,cyChar);
SetCaretPos(xCaret*cxChar,yCaret*cyChar);
ShowCaret(hwnd);
return 0;
case WM_KILLFOCUS:
//隐藏和干掉插入符号
HideCaret(hwnd);
DestroyCaret();
return 0;
处理击键消息 WM_KEYDOWM,表示某个键的按下。VK_表示虚拟键盘的意思
case WM_KEYDOWN:
switch(wParam)
{
case VK_HOME:
xCaret=0;
break;
case VK_END:
xCaret=cxBuffer-1;
break;
case VK_PRIOR:
yCaret=0;
break;
case VK_NEXT:
yCaret=cyBuffer-1;
break;
case VK_LEFT:
xCaret=max(xCaret-1,0); //最小值为0
break;
case VK_RIGHT:
xCaret=min(xCaret+1,cxBuffer-1); //最大值为cxBuffer-1
break;
case VK_UP:
yCaret=max(yCaret-1,0);
break;
case VK_DOWN:
yCaret=min(yCaret+1,cyBuffer-1);
break;
case VK_DELETE:
for(x=xCaret;x<cxBuffer-1;x++)
BUFFER(x,yCaret)=BUFFER(x+1,yCaret);
BUFFER(cxBuffer-1,yCaret)=' '; //向前移动后完,末尾要补空白
HideCaret(hwnd);
hdc=GetDC(hwnd);
SelectObject(hdc,CreateFont(0,0,0,0,0,0,0,0,
dwCharSet,0,0,0,FIXED_PITCH,NULL));
TextOut(hdc,xCaret*cxChar,yCaret*cyChar, //上面只是改变了数组的内容,这一步是显示,刷新客户区的内容
&BUFFER(xCaret,yCaret), //&BUFFER(xCaret,yCaret)取当前位置的地址,表示当前位置到末尾的内容
cxBuffer-xCaret);
DeleteObject(SelectObject(hdc,GetStockObject(SYSTEM_FONT)));
ReleaseDC(hwnd,hdc);
ShowCaret(hwnd);
break;
}
SetCaretPos(xCaret*cxChar,yCaret*cyChar); //这里统一刷新插入符号的位置
return 0;
这里主要观察窗口过程如何处理message,wParam,lParam,可见message主要表名消息类型,wParam,lParam则具体消息内容
case WM_CHAR:
for(i=0;i<(int)LOWORD(lParam);i++)
{
switch(wParam)
{
case '\b': //空格 ,但这里是删除后流出空格
if(xCaret>0)
{
xCaret--;
SendMessage(hwnd,WM_KEYDOWN,VK_DELETE,1);
}
break;
case '\t': //tab键,也是删除后继内容,而不是整体移动。
do
{
SendMessage(hwnd,WM_CHAR,' ',1);
}
while(xCaret%8!=0);
break;
case '\n':
if(++yCaret==cyBuffer) //换行
yCaret=0;
break;
case '\x1B':
for(y=0;y<cyBuffer;y++)
for(x=0;x<cxBuffer;x++)
BUFFER(x,y)=' ';
xCaret=0;
yCaret=0;
InvalidateRect(hwnd,NULL,FALSE);
break;
default:
BUFFER(xCaret,yCaret)=(TCHAR)wParam; //字母输入
HideCaret(hwnd);
hdc=GetDC(hwnd);
SelectObject(hdc,CreateFont(0,0,0,0,0,0,0,0,
dwCharSet,0,0,0,FIXED_PITCH,NULL));
TextOut(hdc,xCaret*cxChar,yCaret*cyChar, //单个字母
&BUFFER(xCaret,yCaret),1);
DeleteObject(SelectObject(hdc,GetStockObject(SYSTEM_FONT)));
ReleaseDC(hwnd,hdc);
ShowCaret(hwnd);
if(++xCaret==cxBuffer)
{
xCaret=0;
if(++yCaret==cyBuffer)
yCaret=0;
}
break;
}
}
SetCaretPos(xCaret*cxChar,yCaret*cyChar);
return 0;
case WM_PAINT:
hdc=BeginPaint(hwnd,&ps);
SelectObject(hdc,CreateFont(0,0,0,0,0,0,0,0,
dwCharSet,0,0,0,FIXED_PITCH,NULL));
for(y=0;y<cyBuffer;y++) //更新客户区所有内容
TextOut(hdc,0,y*cyChar,&BUFFER(0,y),cxBuffer);
DeleteObject(SelectObject(hdc,GetStockObject(SYSTEM_FONT)));
EndPaint(hwnd,&ps);
return 0;