//.. 按键操作
//双击导入键盘库//提供模拟键盘输入相关操作
import win; 
import mouse;
import key.VK
import key.api; 

namespace key;

::MapVirtualKey = ::User32.api("MapVirtualKey","INT( INT uCode, INT uMapType )" ); 
::keybd_event =  User32.api("keybd_event","void(BYTE bVk,BYTE bScan,INT dwFlags,INT dwExtraInfo)");

delay = win?win.delay: sleep;
delayPress = 5;
delayDown = 5;
delayUp = 1;
 
getName = function(vk,ext){
    return ( ext ? VK_EXT2NAME[vk]) : VK_NAME[vk] 
}

upper = ..string.upper;
getCode = function(kn){
	var vk = (type(kn)==type.number)?kn:VK[upper(kn)] : VK_MAPSHIFT[kn]
	return (type(vk)==type.number)?vk:null,VK_EXT[vk] : VK_EXT2[kn] : 0
} 

char = function(n){
	var str = tostring(n);
	if(#str==2)str = "0"+str;
	down("ALT")
	for(i=1;#str;1){
		press("NUMPAD"+str[[i]]);
	}
	up("ALT")
}

sendUnicode = function(str,tk){
	//如果传入的已经是Unicode, string.toUnicode可以自动识别,不会重复转换
	str = ..string.toUnicode(str);
	
	//unicode 以'\0\0'结束,因此长度要减2
	for(i=1;#str-2;2){
		char( ( ( ( str[i+1] & 0xFF) << 8) | ( str[i] & 0xFF) ) & 0xFFFF   ) 
	}
	  
}


send = function(str,tk=0){
	var caps  = isCapsLk();
	var vk,c;
	var s;
 
	for(i=1;#str){ 
		s = str[[i]];
		vk = VK_MAPSHIFT[ s ];
    	if(vk){
    		pressEx(0x10/*_VK_SHIFT*/,vk); 
    	}
    	else{
    		c = str[i]
    		if( c >= 0x80){
    			char( ( ( ( str[i] & 0xFF) << 8) | ( str[i+1] & 0xFF) ) & 0xFFFF   )
    			i++; 
    			continue;
    		}   
    		capsLk( c >=  'A'#  && c <=  'Z'# ) 
    		if(c >=  'a'#  && c <=  'z'#)
    			press(c-32);
    		else
    			press(s); 
    	}
    	delay(tk);
	} 
	capsLk(caps)
}
 
combine = function(...){
	var vk,ext;
	var args ={ ... }; 
	
    for(i=1;#args;1){
    	vk,ext = getCode(args[i]); 
    	if(!vk)error("输入的键名无效:" + args[i],2); 
       ::keybd_event( vk, ::MapVirtualKey(  vk ,0), ext | 8, 0 );  
       delay(1)
    }
    
    delay( delayDown );
    
    for(i=1;#args;1){
        vk,ext = getCode(args[i]); 
		::keybd_event( vk, ::MapVirtualKey(  vk ,0), ext |  0x2/*_KEYEVENTF_KEYUP*/ | 8, 0 );
		delay(1)
    }
    delay( delayPress );
}
pressEx = combine

press = function(  ... ){
	//模拟键盘按键  
	var vk,ext;
    var args ={ ... };
    for(i=1;#args;1){ 
    	vk,ext = getCode(args[i]);  
    	if(!vk)error("输入的键名无效:" + args[i],2); 
        ::keybd_event( vk, ::MapVirtualKey(  vk ,0), ext | 8, 0 );
        delay( delayDown );
	 	::keybd_event( vk, ::MapVirtualKey(  vk ,0), ext |  0x2/*_KEYEVENTF_KEYUP*/ | 8, 0 );
	 	delay( delayPress );
    }
      
}

repeat = function(k,rep=1,tk){
	var vk,ext = getCode(k);
	if(!vk)error("输入的键名无效:" + args[i],2);
	tk :=  delayPress;
	for(i=1;rep){
		 ::keybd_event( vk, ::MapVirtualKey(  vk ,0), ext | 8, 0 );
        delay( delayDown );
	 	::keybd_event( vk, ::MapVirtualKey(  vk ,0), ext |  0x2/*_KEYEVENTF_KEYUP*/ | 8, 0 );
	 	delay( tk ); 
	}
	 
}


down = function( ... ){
	//按下按键
	 var vk,ext;
	 var args = { ... };
	 for( i=1; #args; 1 ){
	 	vk,ext = getCode(args[i]); 
	 	if(!vk)error("输入的键名无效:" + args[i],2); 
	 	::keybd_event( vk, ::MapVirtualKey(  vk ,0), ext   | 8, 0 );
	 	delay(1);
	}
	delay( delayDown );
}

up = function( ... ){
	//弹起按键
	var vk,ext;
	 var args = { ... }; 
	 for( i=1; #args; 1 ){
	 	vk,ext = getCode(args[i]); 
	 	if(!vk)error("输入的键名无效:" + args[i],2); 
	 	::keybd_event( vk, ::MapVirtualKey(  vk ,0), ext |  0x2/*_KEYEVENTF_KEYUP*/ | 8, 0 );
	 	delay(1);
	}
	delay( delayUp );
}

waitUp = function(k){
	var vk = getCode(k); 
    if(!vk)error("输入的键名无效:" + k,2); 
    
    //必须放开
    while( ( ::GetAsyncKeyState( vk ) < 0) ){ 
		..win.delay(1); //就一直等待
	}
	
}


wait = function( k ){ 
    var vk = getCode(k); 
    if(!vk)error("输入的键名无效:" + k,2); 
    
    var state =  ::GetAsyncKeyState(  vk );
    
    //必须放开
    while( ( ::GetAsyncKeyState( vk ) < 0) ){ 
		..win.delay(1); //就一直等待
	}
    
    //必须按下
 	while( ::GetAsyncKeyState(  vk ) >= 0 ){
 			..win.delay(1);
	}
	
	//必须再次放开
	while( ( ::GetAsyncKeyState( vk ) < 0) ){
		 //如果没有放开
		..win.delay(1); //就一直等待
	}
  

}

getState = function( k ){
	//检测键盘按键状态
	var vk  = getCode(k); 
	if(!vk)error("输入的键名无效:" + k,2); 
	
	var s = ::GetKeyState(vk);
	if(vk== 0x14/*_VK_CAPITAL*/ || vk== 0x90/*_VK_NUMLOCK*/ || vk==0x91/*_VK_SCROLL*/ )
		return ( s & 1 );
	else
		return ( s & 0x8000 == 0x8000),s & 1 == 1;	

}

/*intellisense(key)*/
SetKeyboardState = ::User32.api("SetKeyboardState","int(pointer keyState)")
GetKeyboardState = ::User32.api("GetKeyboardState","int(pointer keyState)")
/*end intellisense*/
 
setState = function( state,... ){ 
	var kbState = ..raw.malloc(256) 
	GetKeyboardState(kbState);
	var c;
	var args = { ... }; 
	for( i=1; #args; 1 ){
	 	vk,ext = getCode(args[i]);
	 	if(!vk)error("输入的键名无效:" + k,2);  
		c = (vk== 0x14/*_VK_CAPITAL*/ || vk== 0x90/*_VK_NUMLOCK*/ || vk==0x91/*_VK_SCROLL*/ ) 
			? 1 : 2_10000000;
			
		if(state)
			kbState[vk+1] |= c;
		else
			kbState[vk+1] &= ~c;
	}
	return SetKeyboardState(kbState);
}

getStateX = function( k ){
	//检测键盘按键状态
	var vk = getCode(k); 
	if(!vk)error("输入的键名无效:" + k,2); 
	
	return ( ::GetAsyncKeyState(vk) & 0x8000 == 0x8000);	
}

isCapsLk = function(){
	return getState(0x14/*_VK_CAPITAL*/ )
}


capsLk = function(lk=true){
	if( ( ::GetKeyState( 0x14/*_VK_CAPITAL*/ ) & 0x01 ) != lk){
		press( 0x14/*_VK_CAPITAL*/  );
		delay( delayPress );
    }
}

scrLk = function(lk=true){
	if( ( ::GetKeyState( 0x91/*_VK_SCROLL*/ ) & 0x01 )!= lk  ){
		press( 0x91/*_VK_SCROLL*/   );
		delay( delayPress );
    }
}

nmLk = function(lk=true){
	if( ( ::GetKeyState( 0x90/*_VK_NUMLOCK*/) & 0x01 )!= lk  ){
		press( 0x90/*_VK_NUMLOCK*/  );
		delay( delayPress );
    }
}

BlockInput = ::User32.api("BlockInput","int(bool fBlockIt)") 
block = function(blockIt=true){
	BlockInput(blockIt);
}

 
/**intellisense(key)  
char(__/*字符编码*/) = 直接用编码发送字符,例:\nkey.char(65)
repeat(.(键名或键码,重复次数) = 重复发送指定的击键\n参数可以是键名字,或者按键的虚拟码(_VK前缀常量)
repeat(.(键名或键码,重复次数,间隔毫秒) = 重复发送指定的击键\n参数可以是键名字,或者按键的虚拟码(_VK前缀常量)
send("__/*字符串*/") = 发送字符串,支持单字符键名、中英文字符串
send("__/*字符串*/",100) =  发送字符串,支持单字符键名、中英文字符串\n并在每次按键添加指定的延时(以毫秒为单位)
sendUnicode("__/*字符串*/") = 将字符串转换为Unicode字符串然后发送
sendUnicode("__/*字符串*/",100) =  将字符串转换为Unicode字符串然后发送\n并在每次按键添加指定的延时(以毫秒为单位)
getName(_VK__) = 返回虚拟键码对应的键点
getName(_VK__,true) = 返回虚拟键码对应的扩展键名,例如数字键盘的回车键
getCode("__") = 返回虚拟键码、是否扩展键
pressEx("CTRL","__") = 请使用key.combine替代此函数
combine("CTRL","__") = 发送组合热键,参数个数不限.\n参数可以是键名字,或者按键的虚拟码(_VK前缀常量)
press(__) = 发送按键,参数个数不限\n参数可以是键名字,或者按键的虚拟码(_VK前缀常量)
down(__) = 发送键按下指令,参数个数不限\n参数可以是键名字,或者按键的虚拟码(_VK前缀常量)
up(__) = 发送键弹起指令,参数个数不限\n参数可以是键名字,或者按键的虚拟码(_VK前缀常量)
wait(__) = 等待指定按键\n参数可以是键名字,或者按键的虚拟码(_VK前缀常量
waitUp(__) = 等待一个按下的键放开\n参数可以是键名字,或者按键的虚拟码(_VK前缀常量
getStateX(__) = 返回指定物理按键的按下弹起状态(即使是其他程序拥有键盘焦点)\n该函数不能检测按键开关状态(nmlk,scrlk,capslk)
getState(__) = 自当前程序键盘消息队列返回指定逻辑按键的按下弹起状态,开关状态\n对于NumLk,CapsLk,ScrLk仅返回按键的开关状态
setState(true,__/*键名或键码*/) = 设置一个或多个键为按下状态\nNumLk,CapsLk,ScrLk等键则置为开启状态
setState(false,__/*键名或键码*/)  = 设置一个或多个键为松开状态\nNumLk,CapsLk,ScrLk等键则置为关闭状态
isCapsLk() = 判断Caps Lock键是否打开大写状态
nmLk() = 打开Num Lock(数字键盘功能切换)
nmLk() = 关闭Num Lock(数字键盘功能切换)
scrLk() = 打开Scroll Lock(翻页键功能切换)
scrLk(false) = 关闭Scroll Lock(翻页键功能切换)
capsLk() = 打开Caps Lock,切找到大写状态
capsLk(false) = 关闭Caps Lock,切换到小写状态
block() = 锁定键盘鼠标
block(false) = 解锁键盘鼠标
delayPress = 更改每次击键并松开后的时间间隔,默认为5毫秒
delayDown = 每次调用key.down函数的默认时间间隔,默认为5毫秒\n在调用press以及pressEx函数时,按下松开键之间也会按此设定延时
delayUp = 每次调用key.up函数后的时间间隔,默认为1毫秒
delay = 指定延时函数
end intellisense**/

