C語言判斷鍵盤按下和釋放,是通過檢測該鍵的狀態(tài)來實現(xiàn)的。如果是單一的一個按鍵,那么直接檢測該鍵是“1”還是“0”,就可以確定是按下還是釋放。如果是矩陣鍵盤,那么需要發(fā)送掃描碼,再檢測輸入值,來判斷鍵盤中鍵的狀態(tài)。以下舉例說明:
員工經(jīng)過長期磨合與沉淀,具備了協(xié)作精神,得以通過團(tuán)隊的力量開發(fā)出優(yōu)質(zhì)的產(chǎn)品。創(chuàng)新互聯(lián)建站堅持“專注、創(chuàng)新、易用”的產(chǎn)品理念,因為“專注所以專業(yè)、創(chuàng)新互聯(lián)網(wǎng)站所以易用所以簡單”。公司專注于為企業(yè)提供成都網(wǎng)站設(shè)計、做網(wǎng)站、微信公眾號開發(fā)、電商網(wǎng)站開發(fā),小程序設(shè)計,軟件按需求定制設(shè)計等一站式互聯(lián)網(wǎng)企業(yè)服務(wù)。
bit?keychk()????//單一按鍵檢測
{
if(P1.0==0)????//如果鍵按下
{
delay();?????//延時去抖
if(P1.0==0)return(1);????//返回鍵狀態(tài)
}
return(0);
}
unsigned?char?kbscan(void)????//矩陣掃描按鍵檢測
{
unsigned?char?sccode,recode;
P1=0x0f;??//發(fā)0掃描,列線輸入
if?((P2??0x0f)?!=?0x0f)??//有鍵按下
{
delay(20);???//延時去抖動
if?((P10x0f)!=?0x0f)
{
sccode?=?0xef;????//逐行掃描初值
while((sccode0x01)!=0)
{
P1=sccode;
if((P10x0f)!=0x0f)
{
recode=(P10x0f)|0xf0;
while((P10x0f)!=0x0f);//等待鍵抬起
return((~sccode)+(~recode));
}
else
sccode=(sccode1)|0x01;
}
}
}
return?0;??//無鍵按下,返回0
}
綜觀TC提供的鍵盤輸入函數(shù),以bioskey函數(shù)為最合適選擇。
int bioskey(int cmd);
使用BIOS中斷0x16執(zhí)行各種鍵盤操作。參數(shù)cmd確定實際得操作。
bioskey的返回值由cmd決定:
0:低8位非0,返回在隊列中等待的下一輸入鍵的ascii字符或鍵盤的下一次按鍵輸入的ascii字符。低8位為0,則高8位為擴(kuò)展鍵盤碼。
1:測試是否有可讀的輸入鍵,為0,則沒有。Ctrl_break 返回0xffff(-1)。否則,返回下一個輸入鍵。鍵值還保存,供下次cmd=0時bioskey調(diào)用返回。
2:請求當(dāng)前換檔鍵狀態(tài)。狀態(tài)值由下列值相或(or)得到:
位7 0x80 Insert ON
6 0x40 Caps ON
5 0x20 Numlock ON
4 0x10 Scroll Lock ON
3 0x08 ALT
2 0x04 CTRL
1 0x02 - SHIFT
0 0x01 - SHIFT
為了方便起見,我們首先定義一些常用功能鍵的鍵值。
#define ReturnKey 0x0d
#define ESC 0x1b
#define Back 0x08
#define LeftArrow 0x4b00
#define RightArrow 0x4d00
#define UpArrow 0x4800
#define DownArrow 0x5000
#define PageUp 0x4900
#define PageDown 0x5100
#define Home 0x4700
#define End 0x4f00
#define F1 0x3b00
#define F2 0x3c00
#define F3 0x3d00
#define F4 0x3e00
#define F5 0x3f00
#define F6 0x4000
#define F7 0x4100
#define F8 0x4200
#define F9 0x4300
#define F10 0x4400
int GetKey(void)
{
int tKey;
while(bioskey(1)==0)
;
tKey=bioskey(0);
if ((tKey 0xff)!=0)
tKey=tKey 0xff;
return tKey;
}
以上代碼,可基本完成常用程序鍵盤讀取操作。
但我們必須注意到,對于函數(shù)GetKey的定義中,bioskey函數(shù)的使用,我們只是對cmd=0,1的情況進(jìn)行了處理,而對于cmd=2,也即換檔鍵狀態(tài)沒有進(jìn)行處理,因而諸如ctrl-home等等GetKey不能進(jìn)行相應(yīng)的接收,如果確實需要,須繼續(xù)擴(kuò)充GetKey函數(shù)。
首先最簡單的但可以后臺處理的有SendMessage()PostMessage()
發(fā)送鼠標(biāo),
鍵盤消息,
這個百度一搜n多了
然后中等級的模擬就是keyboard_eventmouse_event了這個比較用的比較多,
給個例子你
模擬鍵盤點擊void
Press(UINT
key)
{
keybd_event(key,MapVirtualKey(key,
0),0,0);
keybd_event(key,MapVirtualKey(key,
0),KEYEVENTF_KEYUP,0);
}
模擬
鼠標(biāo)左鍵單擊mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0);
mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0);
這里注意調(diào)用
keyboard_event
的時候一定要用MapVirtualKey
網(wǎng)上大部分的代碼都是沒用的,
這個函數(shù)時獲取
硬件掃描碼
的先說下keybd_event();函數(shù)的參數(shù)keybd_event(要模擬按下的虛擬按鍵碼,
虛擬按鍵碼對應(yīng)的硬件掃描碼,0,0);網(wǎng)上的代碼大部分都有
虛擬按鍵碼,
當(dāng)然你運行大部分都是沒問題的,
因為對于一般的程序而言是沒問題的但有的程序為了防止
外掛
或者
防止其他程序惡意修改什么的,會對
鼠標(biāo)鍵盤
消息進(jìn)行檢測,
如果檢測到?jīng)]有硬件掃描碼會拒絕執(zhí)行的
然后還有比較高級的就是SendInput模擬了void
OnSendCharCode(unsigned
short
unicode
=
0,unsigned
short
vcode
=
0,bool
bDown
=
false,bool
bUnicode
=
true);
void
OnSendCharCode(unsigned
short
unicode,unsigned
short
vcode,bool
bDown,bool
bUnicode)
{
unsigned
short
uNum
=
0;
//
v-code
Clicked
INPUT
事件設(shè)定
KEYBDINPUT
kDown;
KEYBDINPUT
kUp;
if(!bUnicode)
{
//
'A';
(Virtual
Code
只有大寫),
要輸入小寫
//
Enter:
13,
Backspace:
8,
Up:38
kDown.wVk
=
kUp.wVk
=
vcode;
kDown.wScan
=
kUp.wScan
=
::MapVirtualKey(kDown.wVk,0);
//kDown.dwFlags
=
KEYEVENTF_EXTENDEDKEY;
kUp.dwFlags
=
KEYEVENTF_KEYUP;
}else{
kDown.wVk
=
kUp.wVk
=
0;
kDown.wScan
=
kUp.wScan
=
unicode;
kDown.dwFlags
=
KEYEVENTF_UNICODE;
kUp.dwFlags
=
KEYEVENTF_UNICODE
|
KEYEVENTF_KEYUP;
}
//
建立
KeyDown
事件
建立
KeyUP
事件
INPUT
inputKeyDown,
inputKeyUP;
inputKeyDown.type
=
inputKeyUP.type
=
INPUT_KEYBOARD;
//
指定
input
為
keyboard
kDown.time
=
kUp.time
=
0;
//
the
system
will
provide
its
own
time
stamp.
kDown.dwExtraInfo
=
kUp.dwExtraInfo
=
(WORD)::GetMessageExtraInfo();
inputKeyDown.ki
=
kDown;//
指定
鍵盤
屬性結(jié)構(gòu)
inputKeyUP.ki
=
kUp;
//
指定
鍵盤
屬性結(jié)構(gòu)
//
v-code
Clicked
INPUT
事件設(shè)定完成
//
將
INPUT
事件送到
Root
視窗,
系統(tǒng)會根據(jù)視窗位置,送給目標(biāo)視窗
if(bDown)
{
INPUT
event[1]
=
{inputKeyDown};
uNum
=
::SendInput(1,event,
sizeof(INPUT));
}else{
INPUT
event[2]
=
{inputKeyDown,inputKeyUP};
uNum
=
::SendInput(2,event,
sizeof(INPUT));
}
}
//例子:
OnSendCharCode(0,VK_SHIFT,true,false);
OnSendCharCode(0,'A',false,false);
OnSendCharCode(0,VK_SHIFT,false,false);
OnSendCharCode(0,'A',false,false);
//KEYEVENTF_UNICODE
能區(qū)分大小寫
OnSendCharCode('A');
OnSendCharCode('a');
不過還是有很多游戲,它是用
DirectX
技術(shù)去做的,這些大部分對他們沒用所以還有最最最高級的模擬,
就是
對硬件驅(qū)動程序的模擬這個有個外國人寫的
winio.h
的頭文件,
有興趣可以去學(xué),
一般用不到的,
看程序中P1.0-P1.3是行掃描輸出,P1.4-P1.7是掃描輸入。所以原理是P1.0-P1.3中每次只有一個引腳輸出0,其余輸出1,然后讀取P1.4-P1.7是否有引腳為0;如果有0說明有按鍵按下,如果全1說明沒有按鍵按下。
scancode這個變量就是用于控制P1.0-P1.3(P1?=?scancode;由這句輸出)中每次只有一個引腳輸出0的。
scancode?=?0xfe;這句中bit0值為0,其余bit為1,所以開始時,P1?=?scancode;由這句輸出后,P1.0為0,其余引腳為1。
scancode?=?(scancode??1)?|?0x01;這句使為0的bit左移一位(依次值為0xFE,?0xFD,?0xFB,?0xF7,?0xEF)。0xFE,?0xFD,?0xFB,?0xF7都滿足while?((scancode?0x10)?!=?0)這個條件,而0xEF則不滿足了,所以說此語句只檢查第五位是否為1,如果最低列按下后不就等于零了,就跳過此函數(shù)了,到這就所有按鍵都掃描過了,沒有按鍵按下(如果有按鍵按下,中途就返回了)。
if?((P1?0xf0)?!=?0xf0)這句中P1?0xf0表示只判斷P1.4-P1.7,只有當(dāng)前按下的按鍵所在行掃描輸出為0時,這個表達(dá)式才成立(這時(P1?0xf0)?!=?0xf0);否則這個表達(dá)式肯定不成立(這時(P1?0xf0)?==?0xf0),所以說當(dāng)前行。
return?((~scancode)?+?(~tmpcode));這句就是如果有按鍵按下,那么直接返回鍵值,并不再進(jìn)行循環(huán)退出程序。
程序格式要注意,下面這樣更清楚:
uchar?keyscan(void)
{
uchar?scancode,?tmpcode;
P1?=?0xf0;?//?發(fā)全0行掃描碼
if?((P1?0xf0)?!=?0xf0)
//?若有鍵按下
{
delay();?//?延時去抖動
if?((P1?0xf0)?!=?0xf0)
//?延時后再判斷一次,去除抖動影響
{
scancode?=?0xfe;
while?((scancode?0x10)?!=?0)
//?逐行掃描此語句只檢查第五位是否為1,如果最低列按下后不就等于零了,就跳過此函數(shù)了
{
P1?=?scancode;?//?輸出行掃描碼
if?((P1?0xf0)?!=?0xf0)
//?本行有鍵按下為什么是行?哪一列按下此句都成立啊
{
tmpcode?=?(P1?0xf0)?|?0x0f;
/*?返回特征字節(jié)碼,為1的位即對應(yīng)于行和列?*/
return?((~scancode)?+?(~tmpcode));
}
else
scancode?=?(scancode??1)?|?0x01;
//?行掃描碼左移一位
}
}
}
return?(0);?//?無鍵按下,返回值為0
}
C語言實現(xiàn)的鍵盤檢測:
/*
檢測當(dāng)前是否有鍵盤輸入,并更新鍵值JR_KeyValue
*/
int
JR_AnyKeys(void);
/*
返回鍵值A(chǔ)SCII碼
或
對應(yīng)自定義鍵值
*/
int
JR_GetKeyNum(void);
/*
檢測指定鍵是否有按下
*/
int
JR_IsKeyDown(const
int
key);
BUG:在一次檢測中多次調(diào)用函數(shù)JR_GetKeyNum()就會出錯了。
FIX:把JR_KeyBoard.c文件中的JR_GetKeyNum()函數(shù)里面的第一句“JR_AnyKeys();”注釋掉就可以了。
#include bios.h
#include stdio.h
main()
{
int key;
for(;;)
{
if (bioskey(1)) /*有鍵按下,讀取鍵值*/
key=bioskey(0);
else /*無鍵按下,返回0*/
key=0;
switch(key)
{case 0: ...../*無按鍵,執(zhí)行該操作*/
case 0xXXXXH:..../*不同鍵值,做不同處理*/
。。。。。
}
}
}
該程序段功能:
判斷是否有鍵按下,若有進(jìn)行不同操作,若無進(jìn)行其他操作。
相關(guān)原理:
函數(shù)名: bioskey
功 能: 直接使用BIOS服務(wù)的鍵盤接口
函數(shù)原型:int bioskey (int cmd)
說明:bioskey()的函數(shù)原型在bios.h中
bioskey()完成直接鍵盤操作,cmd的值決定執(zhí)行什么操作。
cmd = 1:
當(dāng)cmd是1,bioskey()查詢是否按下一個鍵,若按下一個鍵則返回非零值,否則返回0。
鍵盤有按鍵時,會把鍵值(掃描碼)存入緩沖區(qū),當(dāng)需要判斷是否有鍵按下時,掃描緩沖區(qū),為空則無鍵按下,不為空則有鍵按下。注意:該功能并不清空緩沖區(qū)。
cmd = 0:
當(dāng)cmd是0,bioskey()返回鍵盤鍵入的值。它返回一個16位的二進(jìn)制數(shù),包括兩個不同的值。當(dāng)按下一個普通鍵時,它的低8位數(shù)存放該字符的ASCII碼,高8位存放該鍵的掃描碼;對于特殊鍵(如方向鍵、F1~F12等等),低8位為0,高8位字節(jié)存放該鍵的掃描碼。
其操作步驟為:
1.檢測緩沖區(qū)是否有數(shù)據(jù);
2.沒有則繼續(xù)第一步;
3.讀取緩沖區(qū)第一個單元中的鍵盤輸入
4.讀取的掃描碼
5.將已讀取的鍵盤輸入從緩沖區(qū)刪除