BSOne.SFC/Tocsg.Lib/VCL/Tocsg.Keyboard.pas

733 lines
20 KiB
Plaintext

{*******************************************************}
{ }
{ 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.