{*******************************************************} { } { Tocsg.Keyboard } { } { Copyright (C) 2022 sunk } { } {*******************************************************} unit Tocsg.Keyboard; interface uses Winapi.Windows, System.Classes, System.SysUtils; function IsCapsLockOn: Boolean; function IsNumLockOn: Boolean; function IsScrollLockOn: Boolean; function GetInputKeyToStr(nInput: Integer): String; inline; function EngCharToHanChar(const cEng: Char): Char; function EngStrToHanStr(const sEng: String; bIgnoreSPKey: Boolean = false): String; procedure DeleteSPKey(var sContexts: String); function ConvHotkeyToStr(dwHotkey: DWORD): String; procedure PressKeys(const sPress: String; bCtrl: Boolean = false); procedure PressKey(const dwCode: DWORD); function IsWinKeyDown: Boolean; implementation uses Vcl.Forms; const ChoSung : WideString = 'ㄱㄲㄴㄷㄸㄹㅁㅂㅃㅅㅆㅇㅈㅉㅊㅋㅌㅍㅎ'; JungSung : WideString = 'ㅏㅐㅑㅒㅓㅔㅕㅖㅗㅘㅙㅚㅛㅜㅝㅞㅟㅠㅡㅢㅣ'; JongSung : WideString = ' ㄱㄲㄳㄴㄵㄶㄷㄹㄺㄻㄼㄽㄾㄿㅀㅁㅂㅄㅅㅆㅇㅈㅊㅋㅌㅍㅎ'; function IsCapsLockOn: Boolean; begin Result := GetKeyState(VK_CAPITAL) = 1; end; function IsNumLockOn: Boolean; begin Result := GetKeyState(VK_NUMLOCK) = 1; end; function IsScrollLockOn: Boolean; begin Result := GetKeyState(VK_SCROLL) = 1; end; function GetInputKeyToStr(nInput: Integer): String; var ShiftState: TShiftState; begin ShiftState := KeyDataToShiftState(0); if GetKeyState(VK_LMENU) < 0 then Include(ShiftState, ssAlt); // 단순 캐스팅으로 뭘 쳤는지 알수 없는 것들은 아래처럼 처리한다. case nInput of 8 : Result := '[Back Space]'; 9 : Result := '[Tab]'; 13 : Result := '[Enter]'; 19 : Result := '[Pause]'; 21 : Result := '[HanYoung]'; 22 : Result := '[Hangul]'; 23, 25 : Result := '[Hanja]'; 27 : Result := '[Esc]'; 32 : Result := ' '; 33 : Result := '[Page Up]'; 34 : Result := '[Page Down]'; 35 : Result := '[End]'; 36 : Result := '[Home]'; 37 : Result := '[Left]'; 38 : Result := '[Up]'; 39 : Result := '[Right]'; 40 : Result := '[Down]'; 44 : Result := '[Print Screen]'; 45 : Result := '[Insert]'; 46 : Result := '[Delete]'; 91 : Result := '[Win Right]'; 92 : Result := '[Win Left]'; 93 : Result := '[Win Menu]'; 96 : Result := '[Num 0]'; 97 : Result := '[Num 1]'; 98 : Result := '[Num 2]'; 99 : Result := '[Num 3]'; 100 : Result := '[Num 4]'; 101 : Result := '[Num 5]'; 102 : Result := '[Num 6]'; 103 : Result := '[Num 7]'; 104 : Result := '[Num 8]'; 105 : Result := '[Num 9]'; 106 : Result := '*'; 107 : Result := '+'; 109 : Result := '-'; 110 : Result := '[. Del]'; 111 : Result := '/'; 112 : Result := '[F1]'; 113 : Result := '[F2]'; 114 : Result := '[F3]'; 115 : Result := '[F4]'; 116 : Result := '[F5]'; 117 : Result := '[F6]'; 118 : Result := '[F7]'; 119 : Result := '[F8]'; 120 : Result := '[F9]'; 121 : Result := '[F10]'; 122 : Result := '[F11]'; 123 : Result := '[F12]'; 144 : Result := '[Num Lock]'; 145 : Result := '[Scroll Lock]'; 186 : if ssShift in ShiftState then Result := ':' else Result := ';'; 187 : if ssShift in ShiftState then Result := '+' else Result := '='; 188 : if ssShift in ShiftState then Result := '<' else Result := ','; 189 : if ssShift in ShiftState then Result := '_' else Result := '-'; 190 : if ssShift in ShiftState then Result := '>' else Result := '.'; 191 : if ssShift in ShiftState then Result := '?' else Result := '/'; 192 : if ssShift in ShiftState then Result := '~' else Result := '`'; 219 : if ssShift in ShiftState then Result := '{' else Result := '"["'; 220 : if ssShift in ShiftState then Result := '|' else Result := '\'; 221 : if ssShift in ShiftState then Result := '}' else Result := '"]"'; 222 : if ssShift in ShiftState then Result := '"' else Result := ''''; 48..57 : if ssShift in ShiftState then begin case nInput of 48 : Result := ')'; 49 : Result := '!'; 50 : Result := '@'; 51 : Result := '#'; 52 : Result := '$'; 53 : Result := '%'; 54 : Result := '^'; 55 : Result := '&'; 56 : Result := '*'; 57 : Result := '('; end; end else Result := IntToStr(nInput - 48); // 숫자 0 ~ 9 65..90 : begin if GetKeyState(VK_CAPITAL) = 0 then begin if ssShift in ShiftState then Result := Char(nInput) else Result := Char(nInput + 32); end else if ssShift in ShiftState then Result := Char(nInput + 32) else Result := Char(nInput); end; end; if ssAlt in ShiftState then Result := '[Alt]+' + Result; if ssCtrl in ShiftState then Result := '[Ctrl]+' + Result; if ssShift in ShiftState then Result := '[Shift]+' + Result; end; function GetChoSungIdx(const cJaso: Char): WORD; begin Result := Pos(cJaso, ChoSung); end; function GetJungSungIdx(const cJaso: Char): WORD; begin Result := Pos(cJaso, JungSung); end; function GetJongSungIdx(const cJaso: Char): WORD; begin Result := Pos(cJaso, JongSung); end; function EngCharToHanChar(const cEng: Char): Char; begin Result := cEng; case cEng of 'A', 'a' : Result := 'ㅁ'; 'B', 'b' : Result := 'ㅠ'; 'C', 'c' : Result := 'ㅊ'; 'D', 'd' : Result := 'ㅇ'; 'e' : Result := 'ㄷ'; 'F', 'f' : Result := 'ㄹ'; 'G', 'g' : Result := 'ㅎ'; 'H', 'h' : Result := 'ㅗ'; 'I', 'i' : Result := 'ㅑ'; 'J', 'j' : Result := 'ㅓ'; 'K', 'k' : Result := 'ㅏ'; 'L', 'l' : Result := 'ㅣ'; 'M', 'm' : Result := 'ㅡ'; 'N', 'n' : Result := 'ㅜ'; 'o' : Result := 'ㅐ'; 'p' : Result := 'ㅔ'; 'q' : Result := 'ㅂ'; 'r' : Result := 'ㄱ'; 'S', 's' : Result := 'ㄴ'; 't' : Result := 'ㅅ'; 'U', 'u' : Result := 'ㅕ'; 'V', 'v' : Result := 'ㅍ'; 'w' : Result := 'ㅈ'; 'X', 'x' : Result := 'ㅌ'; 'Y', 'y' : Result := 'ㅛ'; 'Z', 'z' : Result := 'ㅋ'; 'R' : Result := 'ㄲ'; 'E' : Result := 'ㄸ'; 'Q' : Result := 'ㅃ'; 'T' : Result := 'ㅆ'; 'W' : Result := 'ㅉ'; 'O' : Result := 'ㅒ'; 'P' : Result := 'ㅖ'; end; end; function EngStrToHanStr(const sEng: String; bIgnoreSPKey: Boolean = false): String; const STEP_CHOSUNG = 0; STEP_JUNGSUNG = 1; STEP_JUNGSUNG2 = 2; STEP_JONGSUNG = 3; STEP_JONGSUNG2 = 4; RESULT_SUCCESS = 0; RESULT_FAIL = 1; RESULT_RETRY = 2; RESULT_ADD_RETRY = 3; var i, nLen, nStep: Integer; wCombine, wJasoTemp, wCombineTemp: WORD; cJaso, cNextJaso: Char; bFilterSP: Boolean; function ProcessCombine: Integer; var wTemp: WORD; Label LB_EXIT_STEP_JUNGSUNG2; Label LB_EXIT_STEP_JONGSUNG2; begin Result := RESULT_FAIL; case nStep of STEP_CHOSUNG : begin wTemp := GetChoSungIdx(cJaso); if wTemp > 0 then begin Result := RESULT_SUCCESS; wCombineTemp := wTemp; nStep := STEP_JUNGSUNG; end; end; STEP_JUNGSUNG : begin wTemp := GetJungSungIdx(cJaso); if wTemp > 0 then begin wCombine := ((wCombineTemp - 1) * 21 * 28) + $AC00; Result := RESULT_SUCCESS; case wTemp of 9, // ㅗ 14, // ㅜ 19 : // ㅡ begin wCombineTemp := wTemp; nStep := STEP_JUNGSUNG2; end; else begin Inc(wCombine, (wTemp-1) * 28); nStep := STEP_JONGSUNG; end; end; end; end; STEP_JUNGSUNG2 : begin nStep := STEP_JONGSUNG; // ㅘ, ㅚ, ㅙ, ㅞ,ㅟ... 등등을 처리해 준다 13_1216 16:37:29 sunk wJasoTemp := GetJungSungIdx(cJaso); case wJasoTemp of 1 : // ㅏ case wCombineTemp of 9 : Inc(wCombine, (10-1) * 28); // ㅘ 14 : Goto LB_EXIT_STEP_JUNGSUNG2; 19 : Goto LB_EXIT_STEP_JUNGSUNG2; end; 2 : // ㅐ case wCombineTemp of 9 : Inc(wCombine, (11-1) * 28); // ㅙ 14 : Goto LB_EXIT_STEP_JUNGSUNG2; 19 : Goto LB_EXIT_STEP_JUNGSUNG2; end; 5 : case wCombineTemp of 9 : Goto LB_EXIT_STEP_JUNGSUNG2; 14 : Inc(wCombine, (15-1) * 28); // ㅝ 19 : Goto LB_EXIT_STEP_JUNGSUNG2; end; 6 : // ㅔ case wCombineTemp of 9 : Goto LB_EXIT_STEP_JUNGSUNG2; 14 : Inc(wCombine, (16-1) * 28); // ㅞ 19 : Goto LB_EXIT_STEP_JUNGSUNG2; end; 21 : // ㅣ case wCombineTemp of 9 : Inc(wCombine, (12-1) * 28); // ㅚ 14 : Inc(wCombine, (17-1) * 28); // ㅟ 19 : Inc(wCombine, (20-1) * 28); // ㅢ end; else Goto LB_EXIT_STEP_JUNGSUNG2; end; Result := RESULT_SUCCESS; exit; LB_EXIT_STEP_JUNGSUNG2 : Inc(wCombine, (wCombineTemp-1) * 28); Result := RESULT_RETRY; end; STEP_JONGSUNG : begin if cJaso = ' ' then exit; if (cNextJaso <> #0) and (GetJungSungIdx(EngCharToHanChar(cNextJaso)) > 0) then begin // 종성처리 부분이지만 다음 부분이 중성이면 // 종성처리 하지 않고 초성처리부터 조합하도록 넘긴다 13_1216 16:38:03 sunk nStep := STEP_CHOSUNG; Result := RESULT_ADD_RETRY; exit; end; wTemp := GetJongSungIdx(cJaso); if wTemp > 0 then begin Result := RESULT_SUCCESS; case wTemp of 2, // ㄱ 5, // ㄴ 9, // ㄹ 18 : // ㅂ begin wCombineTemp := wTemp; nStep := STEP_JONGSUNG2; end; else begin Inc(wCombine, wTemp-1); nStep := STEP_CHOSUNG; end; end; end; end; STEP_JONGSUNG2 : if ((cNextJaso <> #0) and (GetJungSungIdx(EngCharToHanChar(cNextJaso)) > 0)) or (GetJungSungIdx(cJaso) > 0) then begin Inc(wCombine, wCombineTemp-1); nStep := STEP_CHOSUNG; Result := RESULT_ADD_RETRY; end else begin // 종성처리 - ㄳ ㄵ ㄶ ㄺ... 등등을 처리해준다. nStep := STEP_CHOSUNG; wJasoTemp := GetJongSungIdx(cJaso); case wJasoTemp of 2 : // ㄱ case wCombineTemp of 2 : Goto LB_EXIT_STEP_JONGSUNG2; // ㄱㄱ 5 : Goto LB_EXIT_STEP_JONGSUNG2; // ㄴㄱ 9 : Inc(wCombine, 10-1); // ㄹㄱ 18 : Goto LB_EXIT_STEP_JONGSUNG2; // ㅂㄱ end; 17 : // ㅁ case wCombineTemp of 2 : Goto LB_EXIT_STEP_JONGSUNG2; // ㄱㅁ 5 : Goto LB_EXIT_STEP_JONGSUNG2; // ㄴㅁ 9 : Inc(wCombine, 11-1); // ㄹㅁ 18 : Goto LB_EXIT_STEP_JONGSUNG2; // ㅂㅁ end; 18 : // ㅂ case wCombineTemp of 2 : Goto LB_EXIT_STEP_JONGSUNG2; // ㄱㅂ 5 : Goto LB_EXIT_STEP_JONGSUNG2; // ㄴㅂ 9 : Inc(wCombine, 12-1); // ㄹㅂ 18 : Goto LB_EXIT_STEP_JONGSUNG2; // ㅂㅂ end; 20 : // ㅅ case wCombineTemp of 2 : Inc(wCombine, 4-1); // ㄱㅅ 5 : Goto LB_EXIT_STEP_JONGSUNG2; // ㄴㅅ 9 : Inc(wCombine, 13-1); // ㄹㅅ 18 : Inc(wCombine, 19-1); // ㅂㅅ end; 23 : // ㅈ case wCombineTemp of 2 : Goto LB_EXIT_STEP_JONGSUNG2; // ㄱㅈ 5 : Inc(wCombine, 6-1); // ㄴㅈ 9 : Goto LB_EXIT_STEP_JONGSUNG2; // ㄹㅈ 18 : Goto LB_EXIT_STEP_JONGSUNG2; // ㅂㅈ end; 26 : // ㅌ case wCombineTemp of 2 : Goto LB_EXIT_STEP_JONGSUNG2; // ㄱㅌ 5 : Goto LB_EXIT_STEP_JONGSUNG2; // ㄴㅌ 9 : Inc(wCombine, 14-1); // ㄹㅌ 18 : Goto LB_EXIT_STEP_JONGSUNG2; // ㅂㅌ end; 27 : // ㅍ case wCombineTemp of 2 : Goto LB_EXIT_STEP_JONGSUNG2; // ㄱㅍ 5 : Goto LB_EXIT_STEP_JONGSUNG2; // ㄴㅍ 9 : Inc(wCombine, 15-1); // ㄹㅍ 18 : Goto LB_EXIT_STEP_JONGSUNG2; // ㅂㅍ end; 28 : // ㅎ case wCombineTemp of 2 : Goto LB_EXIT_STEP_JONGSUNG2; // ㄱㅎ 5 : Inc(wCombine, 7-1); // ㄴㅎ 9 : Inc(wCombine, 16-1); // ㄹㅎ 18 : Goto LB_EXIT_STEP_JONGSUNG2; // ㅂㅎ end; else Goto LB_EXIT_STEP_JONGSUNG2; end; Result := RESULT_SUCCESS; exit; LB_EXIT_STEP_JONGSUNG2 : Inc(wCombine, wCombineTemp-1); Result := RESULT_ADD_RETRY; end; else ASSERT(false); end; end; procedure ProcessAfterCombine; begin // 특수키 제외 시키는 부분에서도 필요해서 따로 뺌 13_1217 16:25:27 sunk case nStep of STEP_JUNGSUNG : Result := Result + ChoSung[wCombineTemp]; // 중성 조합시기에 끝났다면 초성찍고 마무리 13_1217 13:42:40 sunk STEP_JUNGSUNG2 : begin Inc(wCombine, (wCombineTemp-1) * 28); Result := Result + Char(wCombine); end; STEP_JONGSUNG2 : begin Inc(wCombine, wCombineTemp-1); Result := Result + Char(wCombine); end; else if wCombine <> 0 then Result := Result + Char(wCombine); // 조합중인거 있다면 포함시켜서 마무리 13_1217 13:43:31 sunk end; wCombine := 0; nStep := STEP_CHOSUNG; end; begin Result := ''; bFilterSP := false; nStep := 0; nLen := Length(sEng); wCombine := 0; i := 1; while i <= nLen do begin cJaso := EngCharToHanChar(sEng[i]); if (i+1) <= nLen then cNextJaso := EngCharToHanChar(sEng[i+1]) else cNextJaso := #0; if bIgnoreSPKey and not bFilterSP and (cJaso = '[') and (cNextJaso <> '"') then begin ProcessAfterCombine; bFilterSP := true; Result := Result + sEng[i]; Inc(i); continue; end else if bIgnoreSPKey and bFilterSP and (cJaso <> '"') and (cNextJaso = ']') then begin bFilterSP := false; Result := Result + sEng[i] + sEng[i+1]; Inc(i, 2); continue; end else if bFilterSP then begin Result := Result + sEng[i]; Inc(i); continue; end; case ProcessCombine of RESULT_SUCCESS : case nStep of STEP_CHOSUNG : begin Result := Result + Char(wCombine); wCombine := 0; end; end; RESULT_FAIL : if nStep <> STEP_CHOSUNG then begin if nStep = STEP_JUNGSUNG then begin Result := Result + ChoSung[wCombineTemp]; nStep := STEP_CHOSUNG; continue; end else if wCombine <> 0 then begin Result := Result + Char(wCombine) + cJaso; end else Result := Result + cJaso; wCombine := 0; nStep := STEP_CHOSUNG; end else Result := Result + cJaso; RESULT_RETRY : continue; RESULT_ADD_RETRY : begin Result := Result + Char(wCombine); wCombine := 0; continue; end; end; Inc(i); end; // 조합 도중 끝났을때 마무리 ProcessAfterCombine; end; // [Shift], [Ctrl] ... 이렇게 [ ]에 감싼건 다 없애준다. 13_1217 15:19:14 sunk procedure DeleteSPKey(var sContexts: String); var i, nDelBegin: Integer; cOne, cTwo: Char; begin sContexts := StringReplace(sContexts, ']+', ']', [rfReplaceAll]); i := Length(sContexts); nDelBegin := 0; while i > 0 do begin cOne := sContexts[i]; if (i-1) > 0 then cTwo := sContexts[i-1] else cTwo := #0; if (nDelBegin = 0) and (cOne = ']') and (cTwo <> '"') then begin nDelBegin := i; end else if (nDelBegin > 0) and (cTwo = '[') and (cOne <> '"') then begin Delete(sContexts, i-1, nDelBegin - i + 2); Dec(i); nDelBegin := 0; end; Dec(i); end; end; function ConvHotkeyToStr(dwHotkey: DWORD): String; var nHk: Integer; arrChar: array [0..1] of Char; begin Result := ''; ZeroMemory(@arrChar, SizeOf(arrChar)); try if dwHotkey = 0 then exit; if (dwHotkey and $FF) >= $70 then // F1 - F24 begin if ((dwHotkey and $FF) >= $90) then begin case dwHotKey and $FF of $90 : Result := 'Num Lock'; $91 : Result := 'Scroll Lock'; end; end else begin nHk := ((dwHotkey and $FF) - $70) + 1; Result := 'F' + IntToStr(nHk); end; end else begin Result := GetInputKeyToStr(dwHotKey and $FF); end; // if (Result <> '') and ((dwHotKey and $700) > 0) then // Result := ' + ' + Result; // 아래 순서는 일부러 이렇게 함 17_1011 15:29:21 sunk if ((dwHotKey and $400) > 0) then Result := 'Alt + ' + Result; if ((dwHotKey and $100) > 0) then Result := 'Shift + ' + Result; if ((dwHotKey and $200) > 0) then Result := 'Ctrl + ' + Result; finally if Result = '' then Result := 'None'; end; end; procedure PressKeys(const sPress: String; bCtrl: Boolean = false); var i: Integer; u: Byte; begin if bCtrl then keybd_event(VK_CONTROL, 0, 0, 0); try for i := 1 to Length(sPress) do begin u := Byte(sPress[i]); keybd_event(u, MapVirtualKey(u, 0),0, 0); keybd_event(u, MapVirtualKey(u, 0), KEYEVENTF_KEYUP, 0); end; finally if bCtrl then keybd_event(VK_CONTROL, 0, KEYEVENTF_KEYUP, 0); end; end; procedure PressKey(const dwCode: DWORD); begin keybd_event(dwCode, 0, 0, 0); keybd_event(dwCode, 0, KEYEVENTF_KEYUP, 0); end; function IsWinKeyDown: Boolean; begin Result := (GetKeyState(VK_LWIN) and $8000 <> 0) or (GetKeyState(VK_RWIN) and $8000 <> 0); end; end.