Win32 键盘事件 - 击键消息、字符消息、插入符号(光标)

注:以下内容为学习笔记,多数是从书本、资料中得来,只为加深印象,及日后参考。然而本人表达能力较差,写的不好。因非翻译、非转载,只好选原创,但多数乃摘抄,实为惭愧。但若能帮助一二访客,幸甚!


以下内容主要来自《Windows 程序设计》

1.焦点

程序用于从消息队列中读取消息的MSG结构中包含一个hwnd字段。此字段指出了接收消息的窗口句柄。消息循环中的DispatchMessage函数传送消息给需要该消息的窗口过程。

接收到这个键盘事件的窗口称为有输入焦点的窗口。

有时没有窗口具有输入焦点。这种情况发生在所以程序都最小化时。

窗口过程通过捕获WM_SETFOCUS和WM_KILLFOCUS消息来确定自己的窗口是否具有输入焦点。


2.队列和同步

当用户按下和释放键盘上的一个键时,Windows和键盘设备驱动程序将硬件扫描码转换为格式化后的消息。但是这些消息并不立即被放入应用程序消息队列,而是由Windows把这些消息存储在系统消息队列中。系统消息队列是一个单独的消息队列,它被Windows用来初步存储用户从键盘和鼠标输入的消息。仅当Windows应用程序完成了对当前一个用户输入消息的处理后,Windows才从系统消息队列中取出下一条消息,并把它放入应用程序消息队列。


3.击键消息

当用户按下一个键时,Windows将WM_KEYDOWN或WM_SYSKEYDOWN消息放入具有输入焦点的消息队列中。当该键被释放时,Windows把WM_KEYUP或WM_SYSKEYUP消息放入相应的消息队列中。其中SYS代表系统,它表明该击键对Windows比对应用程序更加重要。

虚拟键代码存储在WM_KEYDOWN等消息的wParam参数中,确定哪个键被按下或被释放。

当处理击键消息时,可能需要知道是否有转义键(Shift、Ctrl和Alt)或切换键(Caps Lock、Num Lock和Scroll Lock)键被按下。

可以用如下方式:

iState = GetKeyState(VK_SHIFT);

给前面的SYSMETS代码(http://blog.csdn.net/guzhou_diaoke/article/details/8155740   7.滚动条)中添加键盘处理功能:

简单方法:为窗口过程增加WM_KEYDOWN逻辑

更好的方法:把每一个WM_KEYDOWN消息转换为等同的WM_VSCROLL或WM_HSCROLL消息。可以使用:

SendMessage(hwnd, message, wParam, lParam);

当你调用SendMessage函数时,Windows调用窗口句柄hwnd的窗口过程,同时把四个函数变量传递给他。当窗口过程处理完此消息,Windows把控制权还给紧跟着SendMessage调用的下一条语句。

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	// cxChar平均字符宽度,cyChar字符的总高度(包括外部间距),cxCaps大写字符的平均宽度
	// 等宽字体中,cxCaps等于cxChar,变宽字体中,cxCaps等于cxChar的1.5倍
	static int	cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth, iVscrollPos;	
	HDC			hdc;
	int			i, x, y, iVertPos, iHorzPos, iPaintBeg, iPaintEnd;
	PAINTSTRUCT	ps;
	SCROLLINFO	si;
	TCHAR		szBuffer[10];
	TEXTMETRIC	tm;	

	switch (message)
	{
	case WM_CREATE:
		hdc = GetDC(hwnd);
		
		GetTextMetrics(hdc, &tm);		// 获取系统默认字体的尺寸
		cxChar = tm.tmAveCharWidth;
		// tmPitchAndFamily为1表示变宽字体,为0表示等宽字体
		cxCaps = (tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2;
		cyChar = tm.tmHeight + tm.tmExternalLeading;

		ReleaseDC(hwnd, hdc);

		iMaxWidth = 40*cxChar + 22*cxCaps;
		return 0;

	case WM_SIZE:
		cxClient = LOWORD(lParam);
		cyClient = HIWORD(lParam);
		
		si.cbSize	= sizeof(si);
		si.fMask	= SIF_RANGE | SIF_PAGE;

		si.nMin		= 0;
		si.nMax		= NUMLINES-1;
		si.nPage	= cyClient / cyChar;
		SetScrollInfo(hwnd, SB_VERT, &si, TRUE);

		si.cbSize	= sizeof(si);
		si.fMask	= SIF_RANGE | SIF_PAGE;

		si.nMin		= 0;
		si.nMax		= 2 + iMaxWidth/cxChar;
		si.nPage	= cxClient / cxChar;
		SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);

		return 0;
		
	case WM_VSCROLL:
		si.cbSize	= sizeof(si);
		si.fMask	= SIF_ALL;

		GetScrollInfo(hwnd, SB_VERT, &si);
		iVertPos = si.nPos;

		switch (LOWORD(wParam))
		{
		case SB_TOP:
			si.nPos = si.nMin;
			break;

		case SB_BOTTOM:
			si.nPos = si.nMax;
			break;

		case SB_LINEUP:
			si.nPos -= 1;
			break;

		case SB_LINEDOWN:
			si.nPos += 1;
			break;

		case SB_PAGEUP:
			si.nPos -= si.nPage;
			break;

		case SB_PAGEDOWN:
			si.nPos += si.nPage;
			break;

		case SB_THUMBTRACK:
			si.nPos = si.nTrackPos;
			break;

		default:
			break;
		}

		si.fMask = SIF_POS;
		SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
		GetScrollInfo(hwnd, SB_VERT, &si);

		if (si.nPos != iVertPos)
		{
			ScrollWindow(hwnd, 0, cyChar*(iVertPos-si.nPos), NULL, NULL);
			UpdateWindow(hwnd);
		}
		return 0;

	case WM_HSCROLL:
		si.cbSize	= sizeof(si);
		si.fMask	= SIF_ALL;

		GetScrollInfo(hwnd, SB_HORZ, &si);
		iHorzPos = si.nPos;

		switch (LOWORD(wParam))
		{
		case SB_LINELEFT:
			si.nPos -= 1;
			break;

		case SB_LINERIGHT:
			si.nPos += 1;
			break;

		case SB_PAGELEFT:
			si.nPos -= si.nPage;
			break;

		case SB_PAGERIGHT:
			si.nPos += si.nPage;
			break;

		case SB_THUMBPOSITION:
			si.nPos = si.nTrackPos;
			break;

		default:
			break;
		}

		si.fMask = SIF_POS;
		SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
		GetScrollInfo(hwnd, SB_HORZ, &si);

		if (si.nPos != iHorzPos)
		{
			ScrollWindow(hwnd, cxChar*(iHorzPos-si.nPos), 0, NULL, NULL);
			UpdateWindow(hwnd);
		}
		return 0;

	case WM_KEYDOWN:
		switch (wParam)
		{
		case VK_HOME:
			SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0);
			break;

		case VK_END:
			SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0);
			break;

		case VK_PRIOR:
			SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0);
			break;

		case VK_NEXT:
			SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0);
			break;

		case VK_UP:
			SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0);
			break;

		case VK_DOWN:
			SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0);
			break;

		case VK_LEFT:
			SendMessage(hwnd, WM_HSCROLL, SB_PAGEUP, 0);
			break;

		case VK_RIGHT:
			SendMessage(hwnd, WM_HSCROLL, SB_PAGEDOWN, 0);
			break;
		}
		return 0;

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		si.cbSize = sizeof(si);
		si.fMask = SIF_POS;
		
		GetScrollInfo(hwnd, SB_VERT, &si);
		iVertPos = si.nPos;

		GetScrollInfo(hwnd, SB_HORZ, &si);
		iHorzPos = si.nPos;

		iPaintBeg = max(0, iVertPos + ps.rcPaint.top/cyChar);
		iPaintEnd = min(NUMLINES-1, iVertPos + ps.rcPaint.bottom/cyChar);

		for (i = iPaintBeg; i <= iPaintEnd; i++)
		{
			x = cxChar * (1-iHorzPos);
			y = cyChar * (i-iVertPos);

			TextOut(hdc, x, y, sysmetrics[i].szLabel, lstrlen(sysmetrics[i].szLabel));

			TextOut(hdc, x + 22*cxCaps, y, sysmetrics[i].szDesc, lstrlen(sysmetrics[i].szDesc));

			SetTextAlign(hdc, TA_RIGHT | TA_TOP);

			TextOut(hdc, x + 22*cxCaps + 40*cxChar, y, szBuffer, wsprintf(szBuffer, TEXT("%5d"), 
				GetSystemMetrics(sysmetrics[i].iIndex)));

			// 将对齐方式设回正常方式
			SetTextAlign(hdc, TA_LEFT | TA_TOP);
		}

		EndPaint(hwnd, &ps);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}

	return DefWindowProc(hwnd, message, wParam, lParam);
}


有键盘了,就差鼠标了~很快会有的。


4.字符消息

通过转义状态信息可把击键消息转换为字符消息。

GetMessage从消息队列中取出下一条消息;

TranslateMessage负责把击键消息转换为字符消息;

DispatchMessage调用此消息的窗口过程。


5.消息排序

假如Caps Lock没有锁定,按下再释放A,相应窗口过程会接收:

1)WM_KEYDOWN:’A‘ 的虚拟键代码(0x41)

2)WM_CHAR:  'a'的字符编码(0x61)

3)WM_KEYUP: 'A' 的虚拟键代码(0x41)


按下Shift+A,释放A,再释放Shift:

1)WM_KEYDOWN:虚拟键代码VK_SHIFT(0x10)

2)WM_KEYDOWN:’A‘ 的虚拟键代码(0x41)

3)WM_CHAR:  'A' 的字符编码(0x41)

4)WM_KEYUP: 'A' 的虚拟键代码(0x41)

5)WM_UP:虚拟键代码VK_SHIFT(0x10)


连续按住A:

1)WM_KEYDOWN:’A‘ 的虚拟键代码(0x41)

2)WM_CHAR:  'a'的字符编码(0x61)

3)WM_KEYDOWN:’A‘ 的虚拟键代码(0x41)

4)WM_CHAR:  'a'的字符编码(0x61)

……

n)WM_KEYUP: 'A' 的虚拟键代码(0x41)


6.控制字符的处理

按照以下基本规则来处理击键和字符消息:如果你需要读取输入到窗口中的键盘字符,就处理WM_CHAR消息;如果你需要读取光标键、功能键、Delete键、Insert键、Shift键、Ctrl键和Alt键,则处理WM_KEYDOWN消息。


/*---------------------------------------------------------------------------
	keyView.cpp -- Displays keyboard and character messages
----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------------
	SysMets4.cpp -- System Metrics Display Program ver4 use keyboard
 *----------------------------------------------------------------------------------*/

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
	static TCHAR	szAppName[] = TEXT("KeyView1");
	HWND			hwnd;
	MSG				msg;
	WNDCLASS		wndclass;

	wndclass.style			= CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc	= WndProc;
	wndclass.cbClsExtra		= 0;
	wndclass.cbWndExtra		= 0;
	wndclass.hInstance		= hInstance;
	wndclass.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName	= NULL;
	wndclass.lpszClassName	= szAppName;

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires windows NT!"), szAppName, MB_ICONERROR);
		return 0;
	}

	hwnd = CreateWindow(szAppName,										// window class name
						TEXT("Keyboard Message Viewer #1"),				// window caption
						WS_OVERLAPPEDWINDOW,							// window style
						CW_USEDEFAULT,									// initial x position
						CW_USEDEFAULT,									// initial y position
						CW_USEDEFAULT,									// initial x size
						CW_USEDEFAULT,									// initial y size
						NULL,											// parent window handle
						NULL,											// window menu handle
						hInstance,										// program instance handle
						NULL);											// creation parameters

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	// cxChar平均字符宽度,cyChar字符的总高度(包括外部间距),cxCaps大写字符的平均宽度
	// 等宽字体中,cxCaps等于cxChar,变宽字体中,cxCaps等于cxChar的1.5倍
	static int		cxChar, cyChar, cxClient, cyClient, cxClientMax, cyClientMax;
	static int		cLinesMax, cLines;
	static PMSG		pmsg;
	static RECT		rectScroll;
	static TCHAR szTop[] = TEXT ("Message        Key       Char     ")
                            TEXT ("Repeat Scan Ext ALT Prev Tran") ;
     static TCHAR szUnd[] = TEXT ("_______        ___       ____     ")
                            TEXT ("______ ____ ___ ___ ____ ____") ;

	static TCHAR*	szFormat[2] = {
		TEXT("%-13s %3d %-15s%c%6u %4d %3s %3s %4s %4s"),
		TEXT("%-13s            0x%04X%1s%c %6u %4d %3s %3s %4s %4s") };
	static TCHAR*	szYes	= TEXT("Yes");
	static TCHAR*	szNo	= TEXT("No");
	static TCHAR*	szDown	= TEXT("Down");
	static TCHAR*	szUp	= TEXT("Up");

	static TCHAR*	szMessage[] = {
		TEXT("WM_KEYDOWN"),		TEXT("WM_KEYUP"),
		TEXT("WM_CHAR"),		TEXT("WM_DEADCHAR"),
		TEXT("WM_SYSKEYDOWN"),	TEXT("WM_SYSKEYUP"),
		TEXT("WM_SYSCHAR"),		TEXT("WM_SYSDEADCHAR") };

	HDC			hdc;
	int			i, iType;
	PAINTSTRUCT	ps;
	TCHAR		szBuffer[128], szKeyName[32];
	TEXTMETRIC	tm;	

	switch (message)
	{
	case WM_CREATE:
	case WM_DISPLAYCHANGE:
		cxClientMax = GetSystemMetrics(SM_CXMAXIMIZED);
		cyClientMax = GetSystemMetrics(SM_CYMAXIMIZED);

		hdc = GetDC(hwnd);
		
		SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
		GetTextMetrics(hdc, &tm);		// 获取系统默认字体的尺寸
		cxChar = tm.tmAveCharWidth;
		cyChar = tm.tmHeight;

		ReleaseDC(hwnd, hdc);

		if (pmsg)
			free(pmsg);

		cLinesMax = cyClientMax / cyChar;
		pmsg = (PMSG)malloc(cLinesMax * sizeof(MSG));
		cLines = 0;

		//return 0;

	case WM_SIZE:
		if (message == WM_SIZE)
		{
			cxClient = LOWORD(lParam);
			cyClient = HIWORD(lParam);
		}
		
		rectScroll.left		= 0;
		rectScroll.right	= cxClient;
		rectScroll.top		= cyChar;
		rectScroll.bottom	= cyChar * (cyClient / cyChar);

		InvalidateRect(hwnd, NULL, TRUE);

		return 0;
		
	case WM_KEYDOWN:
	case WM_KEYUP:
	case WM_CHAR:
	case WM_DEADCHAR:
	case WM_SYSKEYDOWN:
	case WM_SYSKEYUP:
	case WM_SYSCHAR:
	case WM_SYSDEADCHAR:
		for (i = cLinesMax-1; i > 0; i--)
		{
			pmsg[i] = pmsg[i-1];
		}

		pmsg[0].hwnd	= hwnd;
		pmsg[0].message	= message;
		pmsg[0].wParam	= wParam;
		pmsg[0].lParam	= lParam;

		cLines = min(cLines + 1, cLinesMax);

		ScrollWindow(hwnd, 0, -cyChar, &rectScroll, &rectScroll);
		break;
	

	case WM_PAINT:
		hdc = BeginPaint(hwnd, &ps);

		SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
		SetBkMode(hdc, TRANSPARENT);
		TextOut(hdc, 0, 0, szTop, lstrlen(szTop));
		TextOut(hdc, 0, 0, szUnd, lstrlen(szUnd));

		for (i = 0; i < min(cLines, cyClient / cyChar - 1); i++)
		{
			iType = pmsg[i].message == WM_CHAR ||
					pmsg[i].message == WM_SYSCHAR ||
					pmsg[i].message == WM_DEADCHAR ||
					pmsg[i].message == WM_SYSDEADCHAR;

			GetKeyNameText(pmsg[i].lParam, szKeyName, sizeof(szKeyName) / sizeof(TCHAR));

			TextOut(hdc, 0, (cyClient/cyChar - 1 - i) * cyChar, szBuffer, 
				wsprintf(szBuffer, szFormat[iType],
					szMessage[pmsg[i].message - WM_KEYFIRST],
					pmsg[i].wParam,
					(PTSTR)(iType ? TEXT(" ") : szKeyName),
					(TCHAR)(iType ? pmsg[i].wParam: ' '),
					LOWORD(pmsg[i].lParam),
					HIWORD(pmsg[i].lParam) & 0xFF,
					0x01000000 & pmsg[i].lParam ? szYes : szNo,
					0x20000000 & pmsg[i].lParam ? szYes : szNo,
					0x40000000 & pmsg[i].lParam ? szDown: szUp,
					0x80000000 & pmsg[i].lParam ? szUp  : szDown));
		}

		EndPaint(hwnd, &ps);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}

	return DefWindowProc(hwnd, message, wParam, lParam);
}



7.插入符号(就是平时说的光标)

插入符号指明你输入的下一个字符将出现在屏幕上的位置。

相关函数:

CreateCaret:创建和窗口关联的插入符号

SetCaretPos:设置窗口内的插入符号的位置

ShowCaret:显式插入符号

HideCaret:隐藏插入符号

DestroyCaret:销毁插入符号

GetCaretPos:获取插入符号位置

GetCaretBlinkTime、SetCaretBlinkTime:获取、设置插入符号闪烁时间


使用插入符号主要规则:在窗口过程处理WM_SETFOCUS消息时调用CreateCaret,处理WM_KILLFOCUS消息时调用DestroyCaret函数。

创建的插入符号是隐藏的,必须调用ShowCaret使之可见。

要在窗口内绘制某些东西时,它必须调用HideCaret隐藏插入符号。结束绘制后,再调用ShowCaret显式插入符号。

HideCaret效果是叠加的。


一个简单的文本编辑器雏形:

/*----------------------------------------------------------------------------
	typer.cpp -- Typing Program
----------------------------------------------------------------------------*/

/*---------------------------------------------------------------------------
	keyView.cpp -- Displays keyboard and character messages
----------------------------------------------------------------------------*/

/*-----------------------------------------------------------------------------------
	SysMets4.cpp -- System Metrics Display Program ver4 use keyboard
 *----------------------------------------------------------------------------------*/

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

#define BUFFER(x, y)	*(pBuffer + y*cxBuffer + x)

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
	static TCHAR	szAppName[] = TEXT("Typer");
	HWND			hwnd;
	MSG				msg;
	WNDCLASS		wndclass;

	wndclass.style			= CS_HREDRAW | CS_VREDRAW;
	wndclass.lpfnWndProc	= WndProc;
	wndclass.cbClsExtra		= 0;
	wndclass.cbWndExtra		= 0;
	wndclass.hInstance		= hInstance;
	wndclass.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
	wndclass.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground	= (HBRUSH)GetStockObject(WHITE_BRUSH);
	wndclass.lpszMenuName	= NULL;
	wndclass.lpszClassName	= szAppName;

	if (!RegisterClass(&wndclass))
	{
		MessageBox(NULL, TEXT("This program requires windows NT!"), szAppName, MB_ICONERROR);
		return 0;
	}

	hwnd = CreateWindow(szAppName,										// window class name
						TEXT("Typer"),									// window caption
						WS_OVERLAPPEDWINDOW,							// window style
						CW_USEDEFAULT,									// initial x position
						CW_USEDEFAULT,									// initial y position
						CW_USEDEFAULT,									// initial x size
						CW_USEDEFAULT,									// initial y size
						NULL,											// parent window handle
						NULL,											// window menu handle
						hInstance,										// program instance handle
						NULL);											// creation parameters

	ShowWindow(hwnd, iCmdShow);
	UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	static DWORD	dwCharSet = DEFAULT_CHARSET;
	static int		cxChar, cyChar, cxClient, cyClient, cxBuffer, cyBuffer, xCaret, yCaret;
	static TCHAR*	pBuffer = NULL;

	int				x, y, i;
	HDC				hdc;
	PAINTSTRUCT		ps;
	TEXTMETRIC		tm;	

	switch (message)
	{
	case WM_INPUTLANGCHANGE:
		dwCharSet = wParam;

	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);

	case WM_SIZE:
		if (message == WM_SIZE)
		{
			cxClient = LOWORD(lParam);
			cyClient = HIWORD(lParam);
		}
		
		cxBuffer = max(1, cxClient/cxChar);
		cyBuffer = max(1, cyClient/cyChar);

		if (pBuffer != NULL)
			free(pBuffer);

		pBuffer = (TCHAR *)malloc(sizeof(TCHAR) * cxBuffer * cyBuffer);

		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;
		
	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);
			break;

		case VK_RIGHT:
			xCaret = min(xCaret+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), cxBuffer-xCaret);

			DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));
			ReleaseDC(hwnd, hdc);
			ShowCaret(hwnd);
			break;
		}
		SetCaretPos(xCaret*cxChar, yCaret*cyChar);
		return 0;

	case WM_CHAR:
		for (i = 0; i < (int)LOWORD(lParam); i++)
		{
			switch (wParam)
			{
			case '\b':			// backspace
				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':			// line feed
				if (++yCaret == cyBuffer)
					yCaret = 0;
				break;

			case '\r':			// carriage return
				xCaret = 0;
				if (++yCaret == cyBuffer)
					yCaret = 0;
				break;

			case '\x1B':		// escape
				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:			// character codes
				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;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;
	}

	return DefWindowProc(hwnd, message, wParam, lParam);
}



  • 2
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值