很多高端機械鍵盤,支持宏定義,例如我們可以設置"D"鍵為"dota",這樣當我們按一下宏開啟鍵,再按一下"D"鍵,就等價於分別按了"d" "o" "t" "a"四個鍵。這時就可以把一些敲代碼時常用的模板定義成鍵盤宏,到時候一鍵補全代碼,既高效又裝X。另外,玩游戲時想按出“下前下前拳”這樣的組合技能也容易多了。
那么問題來了。。
山里來的買不起機械鍵盤的窮B同時又是程序員應該怎么辦。。
其實這樣簡單的功能不一定非要硬件支持,借助一些現有軟件模擬一下鍵盤就好了,在windows下有按鍵精靈和AutoHotKey這些神器,模擬起來很容易,而且體驗非常完美。
我是借助按鍵精靈實現的,按鍵精靈語法很簡單,例如 KeyPress "A", 3 就表示按A鍵3次,而且支持全局快捷鍵啟動,支持監聽用戶輸入,真是簡單到無情。
不過問題又來了。。
鍵盤宏主要是按一系列按鍵,如果每個按鍵都寫一行 KeyPress "X", 1 ,有的還得配合Shift鍵才能按出來,也是累,而且一行一句代碼,看上去不直觀,容易寫錯。
那就寫個代碼生成器就好了,我是用C語言+std::string實現的,直接把宏寫成字符串,生成器自動輸出相應的按鍵,粘貼到按鍵精靈中編譯保存就好了。
貼一下代碼:

1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 #include <ctype.h> 5 #include <string> 6 7 using std::string; 8 9 enum class OpType { 10 Press, 11 Down, 12 Up 13 }; 14 15 enum class CombKey { 16 Shift, 17 Ctrl, 18 Alt 19 }; 20 21 void initTransHash(); //初始化按shift才能打出的字符 22 void transfer(char c); //shift打出的字符轉化為真正的按鍵 23 void procOpType(OpType type); //輸出按鍵操作 24 void callKey(OpType type, char key, int count=1); //按鍵 25 void callKey(OpType type, string key, int count=1); //重載,按功能鍵 26 void keyComb(CombKey comb, char key); //組合鍵 27 28 29 const int delay = 2; 30 string transChar; 31 char transHash[256]; 32 33 34 int main() { 35 initTransHash(); 36 37 string keys = "function() {\n},"; 38 39 for(char key : keys) { 40 if(strchr(transChar.c_str(), key) != NULL) { 41 transfer(key); 42 } else { 43 if(isupper(key)) { 44 keyComb(CombKey::Shift, key); 45 } else { 46 callKey(OpType::Press, key); 47 } 48 } 49 } 50 } 51 52 void initTransHash() { 53 memset(transHash, 0, sizeof(transHash)); 54 55 // 56 // ~ ! @ # $ % ^ & * ( ) _ + { } | : \" < > ? 57 // ` 1 2 3 4 5 6 7 8 9 0 - = [ ] \\ ; ' , . / 58 59 transChar = "~!@#$%^&*()_+{}|:\"<>? \n"; 60 string keys = "`1234567890-=[]\\;',./\0\0"; 61 62 // transChar空格后面的字符不加入hash表 63 for(int i = 0; i < keys.size(); i++) { 64 transHash[transChar[i]] = keys[i]; 65 } 66 } 67 68 void transfer(char c) { 69 if(transHash[c]) { 70 keyComb(CombKey::Shift, transHash[c]); 71 return; 72 } 73 switch(c) { 74 case '\n': 75 callKey(OpType::Press, "Enter"); 76 break; 77 78 case ' ': 79 callKey(OpType::Press, "Space"); 80 break; 81 82 default: 83 printf("[Red] cant transfer: %c\n", c); 84 break; 85 } 86 } 87 88 void procOpType(OpType type) { 89 switch(type) { 90 case OpType::Press: 91 printf("KeyPress "); break; 92 case OpType::Down: 93 printf("KeyDown "); break; 94 case OpType::Up: 95 printf("KeyUp "); break; 96 } 97 } 98 99 void callKey(OpType type, char key, int count) { 100 procOpType(type); 101 printf("\"%c\", %d\n", islower(key) ? key-32 : key, count); 102 printf("Delay %d\n", delay); //每次按鍵后延遲10毫秒 103 } 104 105 void callKey(OpType type, string key, int count) { 106 procOpType(type); 107 //printf("\"%s\"\n", key.c_str()); 108 printf("\"%s\", %d\n", key.c_str(), count); 109 printf("Delay %d\n", delay); //每次按鍵后延遲10毫秒 110 } 111 112 void keyComb(CombKey comb, char key) { 113 string combKey; 114 switch(comb) { 115 case CombKey::Shift: 116 combKey = "Shift"; break; 117 case CombKey::Ctrl: 118 combKey = "Ctrl"; break; 119 case CombKey::Alt: 120 combKey = "Alt"; break; 121 default: 122 return; 123 } 124 125 callKey(OpType::Down, combKey); 126 callKey(OpType::Press, key); 127 callKey(OpType::Up, combKey); 128 }
然后把每一個鍵盤宏寫成一個函數,通過按下按鍵精靈的全局快捷鍵啟動,然后腳本監聽用戶按鍵,然后調用相應函數執行鍵盤宏就可以了,模板類似這樣:

1 choose = WaitKey() 2 TracePrint choose 3 4 Select Case choose 5 Case 70 6 KeyPress "BackSpace", 1 7 Call F() 8 Case 72 9 KeyPress "BackSpace", 1 10 Call H() 11 End Select 12 13 // 按下F鍵 14 Sub F() 15 End Sub 16 17 // 按下H鍵 18 Sub H() 19 End Sub
(宏鍵也會打印出來,所以我在調用函數打印鍵盤宏之前,調用BackSpace刪除了宏鍵的字符)
然后問題又來了。。
上班用的是Mac,Mac沒有按鍵精靈呀。最簡單的辦法是安裝windows虛擬機,物理機和虛擬機共享代碼目錄,在虛擬機中敲代碼,其他工作仍然在物理機中進行,不影響工作流。(其實用了5個月的Mac了,個人感覺,在新鮮感過了之后,實用性比windows差遠了)
另外如果不想通過虛擬機的方式,還可以使用AppleScript,但是AppleScript本身沒有像按鍵精靈那樣的全局啟動快捷鍵,可以通過Automator把AppleScript設置為系統服務,然后給服務設置快捷鍵,但是AppleScript不方便監聽用戶按鍵事件,目前我只能通過dialog讀取用戶輸入,所以做不到按鍵精靈那樣完美,上面的生成器的代碼稍微改動一下,就能作為AppleScript的鍵盤宏生成器了,下面是AppleScript的模板:

1 tell application "System Events" 2 display dialog "choose" default answer "" 3 set ans to text returned of result 4 5 if ans = "p" then 6 keystroke "1" 7 keystroke "0" 8 keystroke "0" 9 keystroke "8" 10 keystroke "6" 11 else 12 display dialog "unknown" 13 end if 14 15 end tell
但是焦點會被dialog獲取,需要在鍵盤宏之前把接收鍵盤宏的進程設為最前。
tell process "XXXXXX" set frontmost to true end tell
還是安裝虛擬機好得多。。