| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566 |
- /******************************************************************************/
- #include "stdafx.h"
- namespace EE{
- /******************************************************************************
- Operate on Scan Codes as Int, valid >=0, invalid <0, because on:
- Mac KB_A has scan code=0
- Android there are scan codes bigger than byte
- /******************************************************************************/
- #if WINDOWS_OLD
- /*
- RightAlt (AltGr on Polish, Norwegian, .. keyboards) also triggers GetKeyState/GetAsyncKeyState VK_LCONTROL
- changing Direct Input exclusive mode (calling 'Unacquire' and 'Acquire') resets the 'GetDeviceState' of any currently pressed keys, they need to be pushed again to activate their state
- Pressing Ctrl+Shift (system shortcut for changing language input) may disable 'GetKeyState' for Ctrl
- Prefer key events instead of 'GetDeviceState' to record pushes/releases within the same frame
- */
- enum COOP_MODE
- {
- BACKGROUND,
- FOREGROUND,
- KEYBOARD_MODE=FOREGROUND, // prefer FOREGROUND to avoid recording input when not focused, maybe BACKGROUND could trigger some anti-virus warnings (potential key-logger), otherwise we could use (_exclusive ? FOREGROUND : BACKGROUND) // prefer background mode so we can get correct information about 'GetDeviceState' when activating the app with LMB (otherwise, the state is delayed by 1 frame), however _exclusive does not support BACKGROUND
- };
- #if !KB_RAW_INPUT
- #define BUF_KEYS 256
- struct DIK
- {
- KB_KEY key;
- Byte dik;
- };
- static const DIK Keys[]= // only keys known to have the same physical location on all keyboard layouts can be listed here
- {
- {KB_LCTRL , DIK_LCONTROL},
- //{KB_RCTRL , DIK_RCONTROL}, processed using WM_*KEY*
- {KB_LSHIFT, DIK_LSHIFT }, // WM_*KEY* does not provide an option to check for left/right shift
- {KB_RSHIFT, DIK_RSHIFT }, // WM_*KEY* does not provide an option to check for left/right shift
- //{KB_LALT , DIK_LALT }, processed using WM_*KEY*
- //{KB_RALT , DIK_RALT }, processed using WM_*KEY*
- //{KB_LWIN , DIK_LWIN }, processed using WM_*KEY*
- //{KB_RWIN , DIK_RWIN }, processed using WM_*KEY*
- {KB_PRINT , DIK_SYSRQ }, // VK_PRINT is not processed because it's assigned as Screen Capture
- };
- #endif
- #elif ANDROID
- /*
- Key characters come from:
- -hardware keyboards (detected only from C++ based on keycodes, 'dispatchKeyEvent' does not detect them)
- -touch keyboards (detected from C++ based on keycodes but without unicode and from 'dispatchKeyEvent')
- To prevent accidental detecting the same key twice, only one source is allowed per frame.
- */
- struct InputText
- {
- Str text;
- VecI2 cur;
- };
- static Bool InputTextIs;
- static InputText InputTextData;
- static SyncLock InputTextLock;
- Byte KeySource;
- #endif
- Keyboard Kb;
- /******************************************************************************/
- #if ANDROID
- static void SetKeyboardVisible(Bool visible)
- {
- #if 0 // not working
- if(AndroidApp && AndroidApp->activity)
- if(visible)ANativeActivity_showSoftInput(AndroidApp->activity, ANATIVEACTIVITY_SHOW_SOFT_INPUT_FORCED); // ANATIVEACTIVITY_SHOW_SOFT_INPUT_IMPLICIT
- else ANativeActivity_hideSoftInput(AndroidApp->activity, 0); // ANATIVEACTIVITY_HIDE_SOFT_INPUT_IMPLICIT_ONLY
- #else
- if(Jni && ActivityClass && Activity)
- {
- #if 1 // activate keyboard using EditText Java object to allow auto-completion
- if(visible)
- {
- C Str *text=&S;
- Int start=0, end=0;
- Bool pass=false;
- if(Gui.kb())switch(Gui.kb()->type())
- {
- case GO_TEXTBOX:
- {
- TextBox &tb=Gui.kb()->asTextBox();
- text =&tb();
- end = tb.cursor();
- start=((tb._edit.sel<0) ? tb.cursor() : tb._edit.sel);
- }break;
- case GO_TEXTLINE:
- {
- TextLine &tl=Gui.kb()->asTextLine();
- text =&tl();
- pass = tl.password();
- end = tl.cursor ();
- start=((tl._edit.sel<0) ? tl.cursor() : tl._edit.sel);
- }break;
- }
- if(JMethodID editText=Jni->GetMethodID(ActivityClass, "editText", "(Ljava/lang/String;IIZ)V"))
- if(JString t=JString(Jni, *text))
- Jni->CallVoidMethod(Activity, editText, t(), jint(start), jint(end), jboolean(pass));
- }else
- {
- if(JMethodID editTextHide=Jni->GetMethodID(ActivityClass, "editTextHide", "()V"))
- Jni->CallVoidMethod(Activity, editTextHide);
- }
- #else
- if(JClass ContextClass="android/content/Context")
- if(JFieldID INPUT_METHOD_SERVICEField=Jni->GetStaticFieldID(ContextClass, "INPUT_METHOD_SERVICE", "Ljava/lang/String;"))
- if(JObject INPUT_METHOD_SERVICE=Jni->GetStaticObjectField(ContextClass, INPUT_METHOD_SERVICEField))
- if(JClass InputMethodManagerClass="android/view/inputmethod/InputMethodManager")
- if(JMethodID getSystemService=Jni->GetMethodID(ActivityClass, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"))
- if(JObject InputMethodManager=Jni->CallObjectMethod(Activity, getSystemService, INPUT_METHOD_SERVICE()))
- if(JMethodID getWindow=Jni->GetMethodID(ActivityClass, "getWindow", "()Landroid/view/Window;"))
- if(JObject window=Jni->CallObjectMethod(Activity, getWindow))
- if(JClass WindowClass="android/view/Window")
- if(JMethodID getDecorView=Jni->GetMethodID(WindowClass, "getDecorView", "()Landroid/view/View;"))
- if(JObject decor_view=Jni->CallObjectMethod(window, getDecorView))
- {
- if(visible)
- {
- if(JMethodID showSoftInput=Jni->GetMethodID(InputMethodManagerClass, "showSoftInput", "(Landroid/view/View;I)Z"))
- Bool ok=Jni->CallBooleanMethod(InputMethodManager, showSoftInput, decor_view(), jint(0));
- }else
- {
- if(JClass ViewClass="android/view/View")
- if(JMethodID getWindowToken=Jni->GetMethodID(ViewClass, "getWindowToken", "()Landroid/os/IBinder;"))
- if(JObject binder=Jni->CallObjectMethod(decor_view, getWindowToken))
- if(JMethodID hideSoftInput=Jni->GetMethodID(InputMethodManagerClass, "hideSoftInputFromWindow", "(Landroid/os/IBinder;I)Z"))
- Bool ok=Jni->CallBooleanMethod(InputMethodManager, hideSoftInput, binder(), jint(0));
- }
- }
- #endif
- }
- #endif
- }
- static void UpdateKeyboardRect()
- {
- if(Kb._visible && !Kb.hwAvailable())
- {
- Kb._recti.set(0, D.resH()/2, D.resW(), D.resH()); // initially set as lower half of the screen
- if(Jni && ActivityClass)
- if(JMethodID getWindow=Jni->GetMethodID(ActivityClass, "getWindow", "()Landroid/view/Window;"))
- if(JObject window=Jni->CallObjectMethod(Activity, getWindow))
- if(JClass WindowClass="android/view/Window")
- if(JMethodID getDecorView=Jni->GetMethodID(WindowClass, "getDecorView", "()Landroid/view/View;"))
- if(JObject decor_view=Jni->CallObjectMethod(window, getDecorView))
- if(JClass ViewClass="android/view/View")
- if(JMethodID getWindowVisibleDisplayFrame=Jni->GetMethodID(ViewClass, "getWindowVisibleDisplayFrame", "(Landroid/graphics/Rect;)V"))
- if(JClass RectClass="android/graphics/Rect")
- if(JMethodID RectCtor=Jni->GetMethodID(RectClass, "<init>", "()V"))
- if(JFieldID left=Jni->GetFieldID(RectClass, "left", "I"))
- if(JFieldID right=Jni->GetFieldID(RectClass, "right", "I"))
- if(JFieldID top=Jni->GetFieldID(RectClass, "top", "I"))
- if(JFieldID bottom=Jni->GetFieldID(RectClass, "bottom", "I"))
- if(JObject r=Jni->NewObject(RectClass, RectCtor))
- {
- Jni->CallVoidMethod(decor_view, getWindowVisibleDisplayFrame, r());
- RectI app_recti(Jni->GetIntField(r, left), Jni->GetIntField(r, top), Jni->GetIntField(r, right), Jni->GetIntField(r, bottom)); // this is the app rect (for example 0,0,1280,800), but we want the keyboard rect
- Int l_size=Max(0, app_recti.min.x-0),
- r_size=Max(0, D.resW()-app_recti.max.x ),
- t_size=Max(0, app_recti.min.y-0),
- b_size=Max(0, D.resH()-app_recti.max.y ), max_size=Max(l_size, r_size, t_size, b_size);
- if(b_size>=max_size)Kb._recti.set( 0, D.resH()-b_size, D.resW(), D.resH());else // bottom size is the biggest
- if(t_size>=max_size)Kb._recti.set( 0, 0, D.resW(), t_size );else // top size is the biggest
- if(l_size>=max_size)Kb._recti.set( 0, 0, l_size , D.resH());else // left size is the biggest
- Kb._recti.set(D.resW()-r_size, 0, D.resW(), D.resH()); // right size is the biggest
- }
- }
- }
- #endif
- /******************************************************************************/
- inline static void Set(KB_KEY key, Char c, Char qwerty_shift, CChar8 *name)
- {
- Kb._key_char[key]=c;
- Kb._key_name[key]=name;
- }
- Keyboard::Keyboard()
- {
- #if 0 // there's only one 'Keyboard' global 'Kb' and it doesn't need clearing members to zero
- _exclusive=_text_input=_refresh_visible=_visible=false;
- #endif
- _last_key_scan_code=-1;
- _imm=true;
- _cur=_last=-1; _last_t=0;
- _curh_tn=0.200f;
- #if 0
- REPA(key_char)Set(KB_KEY(i), '\0', '\0', null);
- #endif
- // set these first in case KB_NPENTER==KB_ENTER, so that KB_ENTER can override the name of KB_NPENTER
- Set(KB_NPDIV , '/' , '/' , "NumPad/");
- Set(KB_NPMUL , '*' , '*' , "NumPad*");
- Set(KB_NPSUB , '-' , '-' , "NumPad-");
- Set(KB_NPADD , '+' , '+' , "NumPad+");
- Set(KB_NPDEL , '\0', '\0', "NumPadDel");
- Set(KB_NPENTER, '\n', '\n', "NumPadEnter");
- Set(KB_NP0, '0', '\0', "NumPad0");
- Set(KB_NP1, '1', '\0', "NumPad1");
- Set(KB_NP2, '2', '\0', "NumPad2");
- Set(KB_NP3, '3', '\0', "NumPad3");
- Set(KB_NP4, '4', '\0', "NumPad4");
- Set(KB_NP5, '5', '\0', "NumPad5");
- Set(KB_NP6, '6', '\0', "NumPad6");
- Set(KB_NP7, '7', '\0', "NumPad7");
- Set(KB_NP8, '8', '\0', "NumPad8");
- Set(KB_NP9, '9', '\0', "NumPad9");
- Set(KB_0, '0', ')', "0");
- Set(KB_1, '1', '!', "1");
- Set(KB_2, '2', '@', "2");
- Set(KB_3, '3', '#', "3");
- Set(KB_4, '4', '$', "4");
- Set(KB_5, '5', '%', "5");
- Set(KB_6, '6', '^', "6");
- Set(KB_7, '7', '&', "7");
- Set(KB_8, '8', '*', "8");
- Set(KB_9, '9', '(', "9");
- Set(KB_A, 'a', 'A', "A");
- Set(KB_B, 'b', 'B', "B");
- Set(KB_C, 'c', 'C', "C");
- Set(KB_D, 'd', 'D', "D");
- Set(KB_E, 'e', 'E', "E");
- Set(KB_F, 'f', 'F', "F");
- Set(KB_G, 'g', 'G', "G");
- Set(KB_H, 'h', 'H', "H");
- Set(KB_I, 'i', 'I', "I");
- Set(KB_J, 'j', 'J', "J");
- Set(KB_K, 'k', 'K', "K");
- Set(KB_L, 'l', 'L', "L");
- Set(KB_M, 'm', 'M', "M");
- Set(KB_N, 'n', 'N', "N");
- Set(KB_O, 'o', 'O', "O");
- Set(KB_P, 'p', 'P', "P");
- Set(KB_Q, 'q', 'Q', "Q");
- Set(KB_R, 'r', 'R', "R");
- Set(KB_S, 's', 'S', "S");
- Set(KB_T, 't', 'T', "T");
- Set(KB_U, 'u', 'U', "U");
- Set(KB_V, 'v', 'V', "V");
- Set(KB_W, 'w', 'W', "W");
- Set(KB_X, 'x', 'X', "X");
- Set(KB_Y, 'y', 'Y', "Y");
- Set(KB_Z, 'z', 'Z', "Z");
- Set(KB_F1 , '\0', '\0', "F1");
- Set(KB_F2 , '\0', '\0', "F2");
- Set(KB_F3 , '\0', '\0', "F3");
- Set(KB_F4 , '\0', '\0', "F4");
- Set(KB_F5 , '\0', '\0', "F5");
- Set(KB_F6 , '\0', '\0', "F6");
- Set(KB_F7 , '\0', '\0', "F7");
- Set(KB_F8 , '\0', '\0', "F8");
- Set(KB_F9 , '\0', '\0', "F9");
- Set(KB_F10, '\0', '\0', "F10");
- Set(KB_F11, '\0', '\0', "F11");
- Set(KB_F12, '\0', '\0', "F12");
- Set(KB_ESC , '\0', '\0', "Escape");
- Set(KB_ENTER, '\n', '\n', "Enter");
- Set(KB_SPACE, ' ' , ' ' , "Space");
- Set(KB_BACK , '\0', '\0', "Backspace");
- Set(KB_TAB , '\t', '\t', "Tab");
- Set(KB_LCTRL , '\0', '\0', "LeftControl");
- Set(KB_RCTRL , '\0', '\0', "RightControl");
- Set(KB_LSHIFT, '\0', '\0', "LeftShift");
- Set(KB_RSHIFT, '\0', '\0', "RightShift");
- Set(KB_LALT , '\0', '\0', "LeftAlt");
- Set(KB_RALT , '\0', '\0', "RightAlt");
- #if APPLE
- Set(KB_LWIN , '\0', '\0', "LeftCmd");
- Set(KB_RWIN , '\0', '\0', "RightCmd");
- #else
- Set(KB_LWIN , '\0', '\0', "LeftWin");
- Set(KB_RWIN , '\0', '\0', "RightWin");
- #endif
- Set(KB_MENU , '\0', '\0', "Menu");
- Set(KB_FIND , '\0', '\0', "Find");
- Set(KB_LEFT , '\0', '\0', "Left");
- Set(KB_RIGHT, '\0', '\0', "Right");
- Set(KB_UP , '\0', '\0', "Up");
- Set(KB_DOWN , '\0', '\0', "Down");
- Set(KB_INS , '\0', '\0', "Insert");
- Set(KB_DEL , '\0', '\0', "Delete");
- Set(KB_HOME, '\0', '\0', "Home");
- Set(KB_END , '\0', '\0', "End");
- Set(KB_PGUP, '\0', '\0', "PageUp");
- Set(KB_PGDN, '\0', '\0', "PageDown");
- Set(KB_SUB , '-' , '_', "-");
- Set(KB_EQUAL , '=' , '+', "=");
- Set(KB_LBR , '[' , '{', "[");
- Set(KB_RBR , ']' , '}', "]");
- Set(KB_SEMICOLON , ';' , ':', ";");
- Set(KB_APOSTROPHE, '\'', '"', "'");
- Set(KB_COMMA , ',' , '<', ",");
- Set(KB_DOT , '.' , '>', ".");
- Set(KB_SLASH , '/' , '?', "/");
- Set(KB_BACKSLASH , '\\', '|', "\\");
- Set(KB_TILDE , '`' , '~', "Tilde");
- Set(KB_CAPS , '\0', '\0', "CapsLock");
- Set(KB_NUM , '\0', '\0', "NumLock");
- Set(KB_SCROLL, '\0', '\0', "ScrollLock");
- Set(KB_PRINT , '\0', '\0', "PrintScreen");
- Set(KB_PAUSE , '\0', '\0', "Pause");
- Set(KB_VOL_DOWN, '\0', '\0', "VolumeDown");
- Set(KB_VOL_UP , '\0', '\0', "VolumeUp");
- Set(KB_VOL_MUTE, '\0', '\0', "VolumeMute");
- Set(KB_NAV_BACK , '\0', '\0', "NavigateBackward");
- Set(KB_NAV_FORWARD, '\0', '\0', "NavigateForward");
- Set(KB_MEDIA_PREV, '\0', '\0', "MediaPrevious");
- Set(KB_MEDIA_NEXT, '\0', '\0', "MediaNext");
- Set(KB_MEDIA_PLAY, '\0', '\0', "MediaPlay");
- Set(KB_MEDIA_STOP, '\0', '\0', "MediaStop");
- Set(KB_ZOOM_IN , '\0', '\0', "ZoomIn");
- Set(KB_ZOOM_OUT, '\0', '\0', "ZoomOut");
- }
- #if MAC
- static void KeyboardChanged(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef user_info) {Kb.setLayout();}
- #endif
- void Keyboard::init() // Linux requires XDisplay to be created, so we can't do this in the constructor
- {
- setLayout();
- #if MAC
- // add callback when keyboard layout changes
- CFNotificationCenterAddObserver(CFNotificationCenterGetDistributedCenter(), null, KeyboardChanged, kTISNotifySelectedKeyboardInputSourceChanged, null, CFNotificationSuspensionBehaviorDeliverImmediately);
- #endif
- }
- /******************************************************************************/
- void Keyboard::del()
- {
- #if WINDOWS_OLD
- #if KB_RAW_INPUT
- RAWINPUTDEVICE rid[1];
- rid[0].usUsagePage=0x01;
- rid[0].usUsage =0x06; // keyboard
- rid[0].dwFlags =RIDEV_REMOVE;
- rid[0].hwndTarget =App.Hwnd();
- RegisterRawInputDevices(rid, Elms(rid), SIZE(RAWINPUTDEVICE));
- #else
- RELEASE(_did);
- #endif
- #endif
- }
- void Keyboard::create()
- {
- if(LogInit)LogN("Keyboard.create");
- #if WINDOWS_OLD
- #if KB_RAW_INPUT
- RAWINPUTDEVICE rid[1];
- rid[0].usUsagePage=0x01;
- rid[0].usUsage =0x06; // keyboard
- rid[0].dwFlags =((KEYBOARD_MODE==BACKGROUND) ? RIDEV_INPUTSINK : 0)|(_exclusive ? RIDEV_NOHOTKEYS : 0);
- rid[0].hwndTarget =App.Hwnd();
- RegisterRawInputDevices(rid, Elms(rid), SIZE(RAWINPUTDEVICE));
- #else
- // DISCL_EXCLUSIVE|DISCL_BACKGROUND is not possible at all
- // DISCL_NOWINKEY |DISCL_BACKGROUND is not possible at all
- // Keyboard doesn't use DISCL_EXCLUSIVE at all, because then the WM_CHAR and WM_KEYDOWN wouldn't be processed
- if(InputDevices.DI)
- if(OK(InputDevices.DI->CreateDevice(GUID_SysKeyboard, &_did, null)))
- {
- if(OK(_did->SetDataFormat(&c_dfDIKeyboard)))
- if(OK(_did->SetCooperativeLevel(App.Hwnd(), (_exclusive ? DISCL_NOWINKEY : 0)|DISCL_NONEXCLUSIVE|((KEYBOARD_MODE==FOREGROUND) ? DISCL_FOREGROUND : DISCL_BACKGROUND))))
- {
- DIPROPDWORD dipdw;
- dipdw.diph.dwSize =SIZE(DIPROPDWORD );
- dipdw.diph.dwHeaderSize=SIZE(DIPROPHEADER);
- dipdw.diph.dwObj =0;
- dipdw.diph.dwHow =DIPH_DEVICE;
- dipdw.dwData =BUF_KEYS;
- _did->SetProperty(DIPROP_BUFFERSIZE, &dipdw.diph);
- if(KEYBOARD_MODE==BACKGROUND)_did->Acquire(); // in background mode we always want the keyboard to be acquired
- goto ok;
- }
- RELEASE(_did);
- }
- ok:;
- #endif
- #endif
- }
- /******************************************************************************/
- Char Keyboard::keyChar(KB_KEY k)C {ASSERT(1<<(8*SIZE(k))==ELMS(_key_char)); return _key_char[k];}
- CChar8* Keyboard::keyName(KB_KEY k)C {ASSERT(1<<(8*SIZE(k))==ELMS(_key_name)); return _key_name[k];}
- /******************************************************************************/
- #if WINDOWS_OLD
- #define KB_F13 KB_NONE
- #define KB_F14 KB_NONE
- #define KB_F15 KB_NONE
- #define KB_F16 KB_NONE
- #define KB_F17 KB_NONE
- #define KB_F18 KB_NONE
- #define KB_F19 KB_NONE
- #if 1 // these keys should be ignored because they're different even though US keyboard was selected, so when US keyboard is selected, then '_qwerty' and 'MapVirtualKey' have 1:1 mapping
- #define KB_PRINT KB_NONE
- #define KB_NP5 KB_NONE
- #define KB_PAUSE KB_NONE
- #define KB_LWIN KB_NONE
- #define KB_RWIN KB_NONE
- #define KB_MENU KB_NONE
- #endif
- static const KB_KEY ScanCodeToQwertyKey[]=
- {
- // 0 1 2 3 4 5 6 7 8 9 A B C D E F
- KB_NONE, KB_ESC , KB_1 , KB_2 , KB_3 , KB_4 , KB_5 , KB_6 , KB_7 , KB_8 , KB_9 , KB_0 , KB_SUB , KB_EQUAL, KB_BACK , KB_TAB , // 0
- KB_Q , KB_W , KB_E , KB_R , KB_T , KB_Y , KB_U , KB_I , KB_O , KB_P , KB_LBR , KB_RBR , KB_ENTER, KB_LCTRL, KB_A , KB_S , // 1
- KB_D , KB_F , KB_G , KB_H , KB_J , KB_K , KB_L , KB_SEMI , KB_APO , KB_TILDE, KB_LSHIFT, KB_BSLASH, KB_Z , KB_X , KB_C , KB_V , // 2
- KB_B , KB_N , KB_M , KB_COMMA , KB_DOT , KB_SLASH, KB_RSHIFT, KB_PRINT, KB_LALT, KB_SPACE, KB_CAPS , KB_F1 , KB_F2 , KB_F3 , KB_F4 , KB_F5 , // 3
- KB_F6 , KB_F7 , KB_F8 , KB_F9 , KB_F10 , KB_NUM , KB_SCROLL, KB_HOME , KB_UP , KB_PGUP , KB_NPSUB , KB_LEFT , KB_NP5 , KB_RIGHT, KB_NPADD, KB_END , // 4
- KB_DOWN, KB_PGDN, KB_INS , KB_DEL , KB_NONE, KB_NONE , KB_NONE , KB_F11 , KB_F12 , KB_PAUSE, KB_NONE , KB_LWIN , KB_RWIN , KB_MENU , KB_NONE , KB_NONE, // 5
- KB_NONE, KB_NONE, KB_NONE, KB_NONE , KB_F13 , KB_F14 , KB_F15 , KB_F16 , KB_F17 , KB_F18 , KB_F19 , KB_NONE , KB_NONE , KB_NONE , KB_NONE , KB_NONE, // 6
- KB_NONE, KB_NONE, KB_NONE, KB_NONE , KB_NONE, KB_NONE , KB_NONE , KB_NONE , KB_NONE, KB_NONE , KB_NONE , KB_NONE , KB_NONE , KB_NONE , KB_NONE , KB_NONE, // 7
- };
- #undef KB_PRINT
- #undef KB_NP5
- #undef KB_PAUSE
- #undef KB_LWIN
- #undef KB_RWIN
- #undef KB_MENU
- #elif MAC
- KB_KEY ScanCodeToKey[Elms(ScanCodeToQwertyKey)];
- const KB_KEY ScanCodeToQwertyKey[0x80]=
- {
- // 0x00
- KB_A,
- KB_S,
- KB_D,
- KB_F,
- KB_H,
- KB_G,
- KB_Z,
- KB_X,
- KB_C,
- KB_V,
- KB_NONE,
- KB_B,
- KB_Q,
- KB_W,
- KB_E,
- KB_R,
- // 0x10
- KB_Y,
- KB_T,
- KB_1,
- KB_2,
- KB_3,
- KB_4,
- KB_6,
- KB_5,
- KB_EQUAL,
- KB_9,
- KB_7,
- KB_SUB,
- KB_8,
- KB_0,
- KB_RBR,
- KB_O,
- // 0x20
- KB_U,
- KB_LBR,
- KB_I,
- KB_P,
- KB_ENTER,
- KB_L,
- KB_J,
- KB_APO,
- KB_K,
- KB_SEMI,
- KB_BSLASH,
- KB_COMMA,
- KB_SLASH,
- KB_N,
- KB_M,
- KB_DOT,
- // 0x30
- KB_TAB,
- KB_SPACE,
- KB_TILDE,
- KB_BACK,
- KB_NONE, // KB_NP_ENTER
- KB_ESC,
- KB_NONE, // KB_RWIN
- KB_NONE, // KB_LWIN
- KB_NONE, // KB_LSHIFT
- KB_NONE, // KB_CAPS
- KB_NONE, // KB_LALT
- KB_NONE, // KB_LCTRL
- KB_NONE, // KB_RSHIFT
- KB_NONE, // KB_RALT
- KB_NONE, // KB_RCTRL
- KB_NONE,
- // 0x40
- KB_NONE,
- KB_NPDEL,
- KB_NONE,
- KB_NPMUL,
- KB_NONE,
- KB_NPADD,
- KB_NONE,
- KB_NUM,
- KB_VOL_UP ,
- KB_VOL_DOWN,
- KB_VOL_MUTE,
- KB_NPDIV,
- KB_NPENTER,
- KB_NONE,
- KB_NPSUB,
- KB_NONE,
- // 0x50
- KB_NONE,
- KB_NONE,
- KB_NP0,
- KB_NP1,
- KB_NP2,
- KB_NP3,
- KB_NP4,
- KB_NP5,
- KB_NP6,
- KB_NP7,
- KB_NONE,
- KB_NP8,
- KB_NP9,
- KB_NONE,
- KB_NONE,
- KB_NONE,
- // 0x60
- KB_F5,
- KB_F6,
- KB_F7,
- KB_F3,
- KB_F8,
- KB_F9,
- KB_NONE,
- KB_F11,
- KB_NONE,
- KB_PRINT,
- KB_NONE,
- KB_SCROLL,
- KB_NONE,
- KB_F10,
- KB_MENU,
- KB_F12,
- // 0x70
- KB_NONE,
- KB_PAUSE,
- KB_INS,
- KB_HOME,
- KB_PGUP,
- KB_DEL,
- KB_F4,
- KB_END,
- KB_F2,
- KB_PGDN,
- KB_F1,
- KB_LEFT,
- KB_RIGHT,
- KB_DOWN,
- KB_UP,
- };
- #elif LINUX
- KB_KEY ScanCodeToKey[Elms(ScanCodeToQwertyKey)];
- const KB_KEY ScanCodeToQwertyKey[0x90]=
- {
- KB_NONE, // 0x00
- KB_NONE, // 0x01
- KB_NONE, // 0x02
- KB_NONE, // 0x03
- KB_NONE, // 0x04
- KB_NONE, // 0x05
- KB_NONE, // 0x06
- KB_NONE, // 0x07
- KB_NONE, // 0x08
- KB_ESC, // 0x09
- KB_1, // 0x0A
- KB_2, // 0x0B
- KB_3, // 0x0C
- KB_4, // 0x0D
- KB_5, // 0x0E
- KB_6, // 0x0F
- KB_7, // 0x10
- KB_8, // 0x11
- KB_9, // 0x12
- KB_0, // 0x13
- KB_SUB, // 0x14
- KB_EQUAL, // 0x15
- KB_BACK, // 0x16
- KB_TAB, // 0x17
- KB_Q, // 0x18
- KB_W, // 0x19
- KB_E, // 0x1A
- KB_R, // 0x1B
- KB_T, // 0x1C
- KB_Y, // 0x1D
- KB_U, // 0x1E
- KB_I, // 0x1F
- KB_O, // 0x20
- KB_P, // 0x21
- KB_LBR, // 0x22
- KB_RBR, // 0x23
- KB_ENTER, // 0x24
- KB_LCTRL, // 0x25
- KB_A, // 0x26
- KB_S, // 0x27
- KB_D, // 0x28
- KB_F, // 0x29
- KB_G, // 0x2A
- KB_H, // 0x2B
- KB_J, // 0x2C
- KB_K, // 0x2D
- KB_L, // 0x2E
- KB_SEMI, // 0x2F
- KB_APO, // 0x30
- KB_TILDE, // 0x31
- KB_LSHIFT, // 0x32
- KB_BACKSLASH, // 0x33
- KB_Z, // 0x34
- KB_X, // 0x35
- KB_C, // 0x36
- KB_V, // 0x37
- KB_B, // 0x38
- KB_N, // 0x39
- KB_M, // 0x3A
- KB_COMMA, // 0x3B
- KB_DOT, // 0x3C
- KB_SLASH, // 0x3D
- KB_RSHIFT, // 0x3E
- KB_NPMUL, // 0x3F
- KB_LALT, // 0x40
- KB_SPACE, // 0x41
- KB_NONE, // 0x42
- KB_F1, // 0x43
- KB_F2, // 0x44
- KB_F3, // 0x45
- KB_F4, // 0x46
- KB_F5, // 0x47
- KB_F6, // 0x48
- KB_F7, // 0x49
- KB_F8, // 0x4A
- KB_F9, // 0x4B
- KB_F10, // 0x4C
- KB_NUM, // 0x4D
- KB_SCROLL, // 0x4E
- KB_NP7, // 0x4F
- KB_NP8, // 0x50
- KB_NP9, // 0x51
- KB_NPSUB, // 0x52
- KB_NP4, // 0x53
- KB_NP5, // 0x54
- KB_NP6, // 0x55
- KB_NPADD, // 0x56
- KB_NP1, // 0x57
- KB_NP2, // 0x58
- KB_NP3, // 0x59
- KB_NP0, // 0x5A
- KB_NPDEL, // 0x5B
- KB_NONE, // 0x5C
- KB_NONE, // 0x5D
- KB_NONE, // 0x5E
- KB_F11, // 0x5F
- KB_F12, // 0x60
- KB_NONE, // 0x61
- KB_NONE, // 0x62
- KB_NONE, // 0x63
- KB_NONE, // 0x64
- KB_NONE, // 0x65
- KB_NONE, // 0x66
- KB_NONE, // 0x67
- KB_NPENTER, // 0x68
- KB_RCTRL, // 0x69
- KB_NPDIV, // 0x6A
- KB_NONE, // 0x6B
- KB_RALT, // 0x6C
- KB_NONE, // 0x6D
- KB_HOME, // 0x6E
- KB_UP, // 0x6F
- KB_PGUP, // 0x70
- KB_LEFT, // 0x71
- KB_RIGHT, // 0x72
- KB_END, // 0x73
- KB_DOWN, // 0x74
- KB_PGDN, // 0x75
- KB_INS, // 0x76
- KB_DEL, // 0x77
- KB_NONE, // 0x78
- KB_NONE, // 0x79
- KB_NONE, // 0x7A
- KB_NONE, // 0x7B
- KB_NONE, // 0x7C
- KB_NONE, // 0x7D
- KB_NONE, // 0x7E
- KB_PAUSE, // 0x7F
- KB_NONE, // 0x80
- KB_NONE, // 0x81
- KB_NONE, // 0x82
- KB_NONE, // 0x83
- KB_NONE, // 0x84
- KB_LWIN, // 0x85
- KB_RWIN, // 0x86
- KB_MENU, // 0x87
- KB_NONE, // 0x88
- KB_NONE, // 0x89
- KB_NONE, // 0x8A
- KB_NONE, // 0x8B
- KB_NONE, // 0x8C
- KB_NONE, // 0x8D
- KB_NONE, // 0x8E
- KB_NONE, // 0x8F
- };
- #endif
- static const KB_KEY VariableKeys[]= // keys that may change depending on layout
- {
- KB_OEM_102, // !! put this first on the list so it's processed last in REPA loop (to avoid selecting it unless necessary, because it's uncommon key) !!
- KB_TILDE,
- KB_0, KB_1, KB_2, KB_3, KB_4, KB_5, KB_6, KB_7, KB_8, KB_9,
- KB_SUB, KB_EQUAL,
- KB_Q, KB_W, KB_E, KB_R, KB_T, KB_Y, KB_U, KB_I, KB_O, KB_P, KB_LBR, KB_RBR, KB_BSLASH,
- KB_A, KB_S, KB_D, KB_F, KB_G, KB_H, KB_J, KB_K, KB_L, KB_SEMI, KB_APO,
- KB_Z, KB_X, KB_C, KB_V, KB_B, KB_N, KB_M, KB_COMMA, KB_DOT, KB_SLASH,
- };
- struct KeyData
- {
- Byte scan_code, cs;
- Char c[3];
- void add(Char c) {T.c[cs++]=CaseUp(c);} // add CaseUp so later when checking for characters we can just do >='A' && <='Z', use Up instead of Down because KB_A is 'A'
- };
- void Keyboard::setLayout()
- {
- REPAO(_qwerty)=KB_KEY(i);
- #if WINDOWS
- #if 1 && WINDOWS_OLD // this is better, but 'MapVirtualKey' is available only on WINDOWS_OLD
- FREPA(ScanCodeToQwertyKey)if(KB_KEY k=ScanCodeToQwertyKey[i])if(UInt vk=MapVirtualKey(i, MAPVK_VSC_TO_VK_EX))if(k!=vk)
- _qwerty[k]=KB_KEY(vk);
- #else
- #if WINDOWS_OLD
- LANG_TYPE lang=LANG_TYPE((UInt(UIntPtr(GetKeyboardLayout(0)))>>16)&0xFF); // LOWORD represents the language, while HIWORD represents the keyboard layout
- #elif WINDOWS_NEW
- LANG_TYPE lang=LanguageCode(Windows::Globalization::Language::CurrentInputMethodLanguageTag->Data());
- #endif
- switch(lang)
- {
- // conversion map was detected by changing to another keyboard layout in the OS, and observing what was received in WM_KEYDOWN while pressing keys
- case LANG_CROATIAN: // LANG_BOSNIAN, LANG_SERBIAN - QWERTZ
- {
- _qwerty[KB_Y ]=KB_Z;
- _qwerty[KB_Z ]=KB_Y;
- _qwerty[KB_SUB ]=KB_SLASH;
- _qwerty[KB_SLASH]=KB_SUB;
- }break;
- case LANG_CZECH: // QWERTZ
- {
- _qwerty[KB_Y ]=KB_Z;
- _qwerty[KB_Z ]=KB_Y;
- _qwerty[KB_SUB ]=KB_EQUAL;
- _qwerty[KB_EQUAL]=KB_SLASH;
- _qwerty[KB_SLASH]=KB_SUB;
- }break;
- case LANG_DANISH: // QWERTY
- {
- _qwerty[KB_TILDE ]=KB_BSLASH;
- _qwerty[KB_SUB ]=KB_EQUAL;
- _qwerty[KB_EQUAL ]=KB_LBR;
- _qwerty[KB_LBR ]=KB_RBR;
- _qwerty[KB_RBR ]=KB_SEMI;
- _qwerty[KB_BSLASH]=KB_SLASH;
- _qwerty[KB_SEMI ]=KB_TILDE;
- _qwerty[KB_SLASH ]=KB_SUB;
- }break;
- case LANG_DUTCH: // AZERTY
- {
- _qwerty[KB_Q ]=KB_A;
- _qwerty[KB_A ]=KB_Q;
- _qwerty[KB_W ]=KB_Z;
- _qwerty[KB_Z ]=KB_W;
- _qwerty[KB_SUB ]=KB_LBR;
- _qwerty[KB_EQUAL]=KB_SUB;
- _qwerty[KB_LBR ]=KB_RBR;
- _qwerty[KB_RBR ]=KB_SEMI;
- _qwerty[KB_SEMI ]=KB_M;
- _qwerty[KB_APO ]=KB_TILDE;
- _qwerty[KB_TILDE]=KB_APO;
- _qwerty[KB_M ]=KB_COMMA;
- _qwerty[KB_COMMA]=KB_DOT;
- _qwerty[KB_DOT ]=KB_SLASH;
- _qwerty[KB_SLASH]=KB_EQUAL;
- }break;
- case LANG_ESTONIAN: // QWERTY
- {
- _qwerty[KB_TILDE]=KB_APO;
- _qwerty[KB_LBR ]=KB_TILDE;
- _qwerty[KB_RBR ]=KB_LBR;
- _qwerty[KB_APO ]=KB_SLASH;
- _qwerty[KB_SLASH]=KB_RBR;
- }break;
- case LANG_FINNISH : // QWERTY
- case LANG_NORWEGIAN: // QWERTY
- case LANG_SWEDISH : // QWERTY
- {
- _qwerty[KB_TILDE ]=KB_BSLASH;
- _qwerty[KB_SUB ]=KB_EQUAL;
- _qwerty[KB_EQUAL ]=KB_LBR;
- _qwerty[KB_LBR ]=KB_RBR;
- _qwerty[KB_RBR ]=KB_SEMI;
- _qwerty[KB_BSLASH]=KB_SLASH;
- _qwerty[KB_SEMI ]=KB_TILDE;
- _qwerty[KB_SLASH ]=KB_SUB;
- }break;
- case LANG_FRENCH: // AZERTY
- {
- _qwerty[KB_Q ]=KB_A;
- _qwerty[KB_A ]=KB_Q;
- _qwerty[KB_W ]=KB_Z;
- _qwerty[KB_Z ]=KB_W;
- _qwerty[KB_SUB ]=KB_LBR;
- _qwerty[KB_LBR ]=KB_RBR;
- _qwerty[KB_RBR ]=KB_SEMI;
- _qwerty[KB_SEMI ]=KB_M;
- _qwerty[KB_APO ]=KB_TILDE;
- _qwerty[KB_TILDE]=KB_APO;
- _qwerty[KB_M ]=KB_COMMA;
- _qwerty[KB_COMMA]=KB_DOT;
- _qwerty[KB_DOT ]=KB_SLASH;
- _qwerty[KB_SLASH]=KB_KEY(223); _qwerty[223]=KB_NONE; // VK_OEM_8 was received in WM_KEYDOWN when pressing '/', need to disable _qwerty[VK_OEM_8] so reverse convert will work
- }break;
- case LANG_GERMAN: // QWERTZ
- {
- _qwerty[KB_Y ]=KB_Z;
- _qwerty[KB_Z ]=KB_Y;
- _qwerty[KB_SUB ]=KB_LBR;
- _qwerty[KB_EQUAL ]=KB_RBR;
- _qwerty[KB_LBR ]=KB_SEMI;
- _qwerty[KB_RBR ]=KB_EQUAL;
- _qwerty[KB_TILDE ]=KB_BACKSLASH;
- _qwerty[KB_BACKSLASH]=KB_SLASH;
- _qwerty[KB_SEMI ]=KB_TILDE;
- _qwerty[KB_SLASH ]=KB_SUB;
- }break;
- case LANG_HUNGARIAN: // QWERTZ
- {
- _qwerty[KB_Y ]=KB_Z;
- _qwerty[KB_Z ]=KB_Y;
- _qwerty[KB_TILDE]=KB_0;
- _qwerty[KB_0 ]=KB_TILDE;
- _qwerty[KB_SUB ]=KB_SLASH;
- _qwerty[KB_SLASH]=KB_SUB;
- }break;
- case LANG_ITALIAN: // QWERTY
- {
- _qwerty[KB_TILDE ]=KB_BSLASH;
- _qwerty[KB_SUB ]=KB_LBR;
- _qwerty[KB_EQUAL ]=KB_RBR;
- _qwerty[KB_LBR ]=KB_SEMI;
- _qwerty[KB_RBR ]=KB_EQUAL;
- _qwerty[KB_BSLASH]=KB_SLASH;
- _qwerty[KB_SEMI ]=KB_TILDE;
- _qwerty[KB_SLASH ]=KB_SUB;
- }break;
- case LANG_LUXEMBOURGISH: // QWERTZ
- {
- _qwerty[KB_Y ]=KB_Z;
- _qwerty[KB_Z ]=KB_Y;
- _qwerty[KB_TILDE]=KB_SLASH;
- _qwerty[KB_SUB ]=KB_LBR;
- _qwerty[KB_EQUAL]=KB_RBR;
- _qwerty[KB_LBR ]=KB_SEMI;
- _qwerty[KB_RBR ]=KB_TILDE;
- _qwerty[KB_SEMI ]=KB_APO;
- _qwerty[KB_APO ]=KB_BSLASH;
- _qwerty[KB_SLASH]=KB_SUB;
- }break;
- case LANG_PORTUGUESE: // QWERTY
- {
- _qwerty[KB_TILDE ]=KB_BSLASH;
- _qwerty[KB_SUB ]=KB_LBR;
- _qwerty[KB_EQUAL ]=KB_RBR;
- _qwerty[KB_LBR ]=KB_EQUAL;
- _qwerty[KB_RBR ]=KB_SEMI;
- _qwerty[KB_BSLASH]=KB_SLASH;
- _qwerty[KB_SEMI ]=KB_TILDE;
- _qwerty[KB_SLASH ]=KB_SUB;
- }break;
- case LANG_SPANISH: // QWERTY
- {
- _qwerty[KB_TILDE ]=KB_BSLASH;
- _qwerty[KB_SUB ]=KB_LBR;
- _qwerty[KB_EQUAL ]=KB_RBR;
- _qwerty[KB_LBR ]=KB_SEMI;
- _qwerty[KB_RBR ]=KB_EQUAL;
- _qwerty[KB_BSLASH]=KB_SLASH;
- _qwerty[KB_SEMI ]=KB_TILDE;
- _qwerty[KB_SLASH ]=KB_SUB;
- }break;
- case LANG_SLOVAK: // QWERTZ
- {
- _qwerty[KB_Y ]=KB_Z;
- _qwerty[KB_Z ]=KB_Y;
- _qwerty[KB_SUB ]=KB_SLASH;
- _qwerty[KB_EQUAL]=KB_KEY(223); _qwerty[223]=KB_NONE; // VK_OEM_8 was received in WM_KEYDOWN when pressing '-', need to disable _qwerty[VK_OEM_8] so reverse convert will work
- _qwerty[KB_SLASH]=KB_SUB;
- }break;
- case LANG_SLOVENIAN: // QWERTZ
- {
- _qwerty[KB_Y ]=KB_Z;
- _qwerty[KB_Z ]=KB_Y;
- _qwerty[KB_SUB ]=KB_SLASH;
- _qwerty[KB_SLASH]=KB_SUB;
- }break;
- case LANG_TURKISH: // QWERTY
- {
- _qwerty[KB_SUB ]=KB_KEY(223); _qwerty[223]=KB_NONE; // VK_OEM_8 was received in WM_KEYDOWN when pressing '-', need to disable _qwerty[VK_OEM_8] so reverse convert will work
- _qwerty[KB_EQUAL ]=KB_SUB;
- _qwerty[KB_BSLASH]=KB_COMMA;
- _qwerty[KB_COMMA ]=KB_SLASH;
- _qwerty[KB_DOT ]=KB_BSLASH;
- _qwerty[KB_SLASH ]=KB_DOT;
- }break;
- }
- #endif
- #elif MAC || LINUX
- Bool ok=false;
- #if MAC
- if(TISInputSourceRef key_layout=TISCopyCurrentKeyboardLayoutInputSource())
- {
- if(CFDataRef unicode_data=(CFDataRef)TISGetInputSourceProperty(key_layout, kTISPropertyUnicodeKeyLayoutData))
- if(UCKeyboardLayout *data=(UCKeyboardLayout*)CFDataGetBytePtr(unicode_data))
- {
- UInt keyboard_type=LMGetKbdType();
- #elif LINUX
- if(XDisplay)
- {
- #endif
- ok=true;
- Bool variable_key[256]; Zero(variable_key); REPA(VariableKeys)variable_key[VariableKeys[i]]=true;
- MemtN<KeyData, Elms(VariableKeys)> kds;
- FREPA(ScanCodeToKey)
- {
- KB_KEY qwerty_key=ScanCodeToQwertyKey[i];
- if(variable_key[qwerty_key]) // if this is a variable key (can change), then get what pressing it will give us (what characters), remember up to 3 combinations, default, with shift, and with right alt
- {
- KeyData kd; kd.cs=0;
- #if MAC
- UniCharCount len; UniChar s[8];
- UInt dead_key_state=0; if(UCKeyTranslate(data, i, kUCKeyActionDown, 0x00, keyboard_type, kUCKeyTranslateNoDeadKeysMask, &dead_key_state, Elms(s), &len, s)==noErr)if(len>0)kd.add(s[0]);
- dead_key_state=0; if(UCKeyTranslate(data, i, kUCKeyActionDown, 0x02, keyboard_type, kUCKeyTranslateNoDeadKeysMask, &dead_key_state, Elms(s), &len, s)==noErr)if(len>0)kd.add(s[0]); // with Shift
- dead_key_state=0; if(UCKeyTranslate(data, i, kUCKeyActionDown, 0x40, keyboard_type, kUCKeyTranslateNoDeadKeysMask, &dead_key_state, Elms(s), &len, s)==noErr)if(len>0)kd.add(s[0]); // with RightAlt
- #elif LINUX
- KeySym ks=XkbKeycodeToKeysym(XDisplay, KeyCode(i), 0, 0); if(ks!=NoSymbol)kd.add(Char(ks));
- ks=XkbKeycodeToKeysym(XDisplay, KeyCode(i), 0, 1); if(ks!=NoSymbol)kd.add(Char(ks)); // Shift
- #endif
- if(kd.cs){kd.scan_code=i; kds.New()=kd;} // if has any character mapping, then store into list to process later
- ScanCodeToKey[i]=KB_NONE; // set as unassigned
- }else ScanCodeToKey[i]=qwerty_key; // assign keys that are not variable
- }
- Bool (&unassigned_key)[256]=variable_key; // we can reuse this array, because only variable keys are unassigned
- // assign characters first, priority #1
- REPAD(ki, kds)
- {
- C KeyData &kd=kds[ki]; REPD(ci, kd.cs)
- {
- Char c=kd.c[ci]; ASSERT(KB_A=='A' && KB_Z=='Z'); if(c>='A' && c<='Z')if(unassigned_key[KB_KEY(c)]){unassigned_key[KB_KEY(c)]=false; ScanCodeToKey[kd.scan_code]=KB_KEY(c); kds.remove(ki); goto next_chr;}
- }
- next_chr:;
- }
- // assign digits, priority #2
- REPAD(ki, kds)
- {
- C KeyData &kd=kds[ki]; REPD(ci, kd.cs)
- {
- Char c=kd.c[ci]; ASSERT(KB_0=='0' && KB_9=='9'); if(c>='0' && c<='9')if(unassigned_key[KB_KEY(c)]){unassigned_key[KB_KEY(c)]=false; ScanCodeToKey[kd.scan_code]=KB_KEY(c); kds.remove(ki); goto next_dig;}
- }
- next_dig:;
- }
- // assign symbols, priority #3
- REPAD(ki, kds)
- {
- C KeyData &kd=kds[ki]; REPD(ci, kd.cs)
- {
- Char c=kd.c[ci]; if(!(c>='A' && c<='Z') && !(c>='0' && c<='9'))REPA(VariableKeys)
- {
- KB_KEY k=VariableKeys[i]; if(Kb._key_char[k]==c)
- {
- if(unassigned_key[k]){unassigned_key[k]=false; ScanCodeToKey[kd.scan_code]=k; kds.remove(ki); goto next_sym;}
- break;
- }
- }
- }
- next_sym:;
- }
- // assign with QWERTY mapping, priority #4
- REPAD(ki, kds)
- {
- C KeyData &kd=kds[ki];
- KB_KEY k=ScanCodeToQwertyKey[kd.scan_code]; if(unassigned_key[k]){unassigned_key[k]=false; ScanCodeToKey[kd.scan_code]=k; kds.remove(ki);}
- }
- // assign remaining to any key that was not assigned yet
- REPAD(ki, kds)
- {
- C KeyData &kd=kds[ki]; REPA(VariableKeys) // !! here use REPA to go from the back, to process KB_OEM_102 last (to avoid selecting it unless necessary, because it's uncommon key) !!
- {
- KB_KEY k=VariableKeys[i]; if(unassigned_key[k]){unassigned_key[k]=false; ScanCodeToKey[kd.scan_code]=k; break;}
- }
- }
- #if MAC
- }
- CFRelease(key_layout);
- }
- #elif LINUX
- }
- #endif
- if(!ok)Copy(ScanCodeToKey, ScanCodeToQwertyKey); // if couldn't obtain keyboard layout, then set as QWERTY
- REPAO(_qwerty)=KB_KEY(i); REPA(ScanCodeToKey)if(KB_KEY qwerty_key=ScanCodeToQwertyKey[i])if(KB_KEY key=ScanCodeToKey[i])_qwerty[qwerty_key]=key;
- #endif
- }
- void Keyboard::swappedCtrlCmd (Bool swapped) {T._swapped_ctrl_cmd=swapped;}
- void Keyboard::requestTextInput( ) {T._text_input =true ;}
- void Keyboard::refreshTextInput( ) {T._refresh_visible =true ;}
- void Keyboard:: setTextInput(C Str &text, Int start, Int end, Bool password)
- {
- #if ANDROID
- if(Jni && ActivityClass && Activity)
- if(JMethodID editTextSet=Jni->GetMethodID(ActivityClass, "editTextSet", "(Ljava/lang/String;IIZ)V"))
- if(JString t=JString(Jni, text))
- Jni->CallVoidMethod(Activity, editTextSet, t(), jint(start), jint(end), jboolean(password));
- #endif
- }
- /******************************************************************************/
- #if WINDOWS_OLD
- Bool Keyboard::imm ( )C { HIMC imc=ImmGetContext (App.Hwnd()); if(imc)ImmReleaseContext(App.Hwnd(), imc); return imc!=null;}
- void Keyboard::imm (Bool enable) {if(_imm!=enable){_imm=enable; ImmAssociateContext(App.Hwnd(), enable ? _imc : null);}}
- Bool Keyboard::immNative( )C {return ImmGetOpenStatus(_imc)!=0;}
- void Keyboard::immNative(Bool native)
- {
- ImmSetOpenStatus(_imc, native);
- if(native)
- {
- LANG_TYPE lang=LANG_TYPE((UInt(UIntPtr(GetKeyboardLayout(0)))>>16)&0xFF); // LOWORD represents the language, while HIWORD represents the keyboard layout
- if(lang==LANG_KOREAN)
- {
- DWORD c, s;
- ImmGetConversionStatus(_imc, &c , &s);
- ImmSetConversionStatus(_imc, c|1, s);
- }
- }
- }
- #else
- Bool Keyboard::immNative( )C {return false;}
- void Keyboard::immNative(Bool native) {}
- #endif
- /******************************************************************************/
- void Keyboard::clear()
- {
- k.clear();
- _last_key_scan_code=-1;
- REPAO(_button)&=~BS_NOT_ON;
- }
- /******************************************************************************/
- INLINE static void AddModifiers(Keyboard::Key &k)
- {
- if(Kb.anyCtrl ())k.flags|=Keyboard::Key::CTRL;
- if(Kb.anyShift())k.flags|=Keyboard::Key::SHIFT;
- if(Kb.anyAlt ())k.flags|=Keyboard::Key::ALT;
- if(Kb.anyWin ())k.flags|=Keyboard::Key::WIN;
- if(Kb.b(KB_LALT))k.flags|=Keyboard::Key::LALT;
- }
- void Keyboard::queue(Char chr, Int scan_code)
- {
- if(Unsigned(chr)>=32)
- {
- if(_last_key_scan_code==scan_code && scan_code>=0 && _key_buffer_len)
- {
- Key &last_key=_key_buffer[(_key_buffer_pos+_key_buffer_len-1)&0xFF];
- if( !last_key.c){last_key.c=chr; return;}
- }
- Key k;
- k.c=chr;
- k.k=KB_NONE;
- k.flags=Key::FIRST;
- AddModifiers(k);
- queue(k); _last_key_scan_code=scan_code;
- }
- }
- void Keyboard::push(KB_KEY key, Int scan_code)
- {
- if(key)
- {
- Key k;
- if(_button[key]&BS_ON) // repeated press
- {
- k.flags=0;
- _button[key]|=BS_REPEAT;
- }else // first press
- {
- k.flags=Key::FIRST;
- _cur =key;
- _button[key]|=BS_PUSHED|BS_ON;
- if(_last==key && Time.appTime()-_last_t<=DoubleClickTime+Time.ad())
- {
- _button[key]|=BS_DOUBLE;
- _last =-1;
- }else
- {
- _last =key;
- _last_t=Time.appTime();
- }
- InputCombo.add(InputButton(INPUT_KEYBOARD, key));
- }
- // !! set modifier flags after adjusting '_button' above !!
- k.c='\0';
- k.k=key;
- AddModifiers(k);
- queue(k); _last_key_scan_code=scan_code;
- }
- }
- void Keyboard::release(KB_KEY key)
- {
- if(_button[key]&BS_ON)
- {
- if(_cur==key)_cur=-1;
- FlagDisable(_button[key], BS_ON );
- FlagEnable (_button[key], BS_RELEASED);
- }
- }
- /******************************************************************************/
- void Keyboard::update()
- {
- #if WINDOWS_OLD
- imm(visibleWanted());
- #if !KB_RAW_INPUT
- if(App.active() && _did)
- {
- DIDEVICEOBJECTDATA didod[BUF_KEYS];
- DWORD elms=BUF_KEYS, ret=_did->GetDeviceData(SIZE(DIDEVICEOBJECTDATA), didod, &elms, 0);
- if(ret==DI_OK || ret==DI_BUFFEROVERFLOW)FREP(elms) // process in order
- {
- C DIDEVICEOBJECTDATA &d=didod[i];
- Byte dik=d.dwOfs;
- REPA(Keys) // process only special keys
- {
- C DIK &key=Keys[i]; if(key.dik==dik)
- {
- if(d.dwData&0x80)push(key.key);else release(key.key);
- break;
- }
- }
- }
- if(ret!=DI_OK) // if we failed to catch entire input, then check most recent state
- {
- Byte dik[256]; if(!OK(_did->GetDeviceState(SIZE(dik), &dik))) // if failed
- {
- _did->Acquire(); // try to re-acquire if lost access for some reason
- if(!OK(_did->GetDeviceState(SIZE(dik), &dik)))Zero(dik); // if still failed, then zero
- }
- REPA(Keys) // process only special keys
- {
- C DIK &key=Keys[i];
- Bool on =(dik[key.dik] || ((key.key==KB_LCTRL) ? _special&1 : GetKeyState(key.key)<0)); // use a combination of both DirectInput and WinApi, because DirectInput loses state when changing exclusive mode (calling 'Unacquire' and 'Acquire'), however we can't use 'GetKeyState' for control (because it can be triggered by AltGr and may be disabled by Ctrl+Shift system shortcut)
- if( on!=FlagTest(_button[key.key], BS_ON))
- {
- if(on)push(key.key);else release(key.key);
- }
- }
- }else // if most recent state wasn't checked
- if(_special&(2|4)) // if we're forcing Shifts, then check if any got released
- {
- if((_special&2) && GetKeyState(VK_LSHIFT)>=0){release(KB_LSHIFT); FlagDisable(_special, 2);}
- if((_special&4) && GetKeyState(VK_RSHIFT)>=0){release(KB_RSHIFT); FlagDisable(_special, 4);}
- }
- }
- #endif
- #elif WINDOWS_NEW
- if(App.active()) // need to manually check for certain keys
- {
- // Shifts may get stuck when 2 pressed at the same time
- if(Kb.b(KB_LSHIFT) && !FlagTest((Int)App.Hwnd()->GetKeyState(Windows::System::VirtualKey:: LeftShift), (Int)Windows::UI::Core::CoreVirtualKeyStates::Down))Kb.release(KB_LSHIFT);
- if(Kb.b(KB_RSHIFT) && !FlagTest((Int)App.Hwnd()->GetKeyState(Windows::System::VirtualKey::RightShift), (Int)Windows::UI::Core::CoreVirtualKeyStates::Down))Kb.release(KB_RSHIFT);
- // not detected through system events
- Bool print=FlagTest((Int)App.Hwnd()->GetKeyState(Windows::System::VirtualKey::Snapshot), (Int)Windows::UI::Core::CoreVirtualKeyStates::Down);
- if( print!=b(KB_PRINT))
- {
- if(print)push(KB_PRINT, 0);else release(KB_PRINT);
- }
- }
- #else
- // keyboard state obtained externally in main loop
- #endif
- #if ANDROID
- // display keyboard
- Bool visible_wanted =visibleWanted();
- if( visible_wanted!=_visible || _refresh_visible){SetKeyboardVisible(_visible=visible_wanted); _refresh_visible=false;}
- UpdateKeyboardRect();
- if(InputTextIs)
- {
- InputText temp; Bool pass=false; Int enters=0; // enter workaround
- {
- SyncLocker locker(InputTextLock);
- if(Gui.kb())switch(Gui.kb()->type())
- {
- case GO_TEXTBOX:
- {
- // no need to apply Enter fix for 'TextBox', because we allow enters there as characters
- TextBox &tb=Gui.kb()->asTextBox();
- tb. setChanged(InputTextData.text);
- tb.cursorChanged(InputTextData.cur.y);
- tb._edit.sel =((InputTextData.cur.x==InputTextData.cur.y) ? -1 : InputTextData.cur.x);
- _key_buffer_len=0; // this is a workaround for a bug in Google/Samsung Keyboard (but not SwiftKey) when Backspace key is triggered even though it shouldn't, when tapping Back key on the soft keyboard (when last character is space, or sometimes when just typed something), in that case 2 Back's are processed (one from EditText Java_com_esenthel_Native_text and one AINPUT_EVENT_TYPE_KEY), this code removes all queued keys to remove KB_BACK
- }break;
- case GO_TEXTLINE:
- {
- // some soft keyboards (GBoard) may generate enter keys as characters, this is a workaround for that
- {
- REPA(InputTextData.text)if(InputTextData.text[i]=='\n')
- {
- enters++; // increase enter counter
- InputTextData.text.remove(i); // remove this character
- if(InputTextData.cur.x>i)InputTextData.cur.x--; // adjust cursor
- if(InputTextData.cur.y>i)InputTextData.cur.y--; // adjust cursor
- }
- }
- TextLine &tl=Gui.kb()->asTextLine();
- if(tl.setChanged(InputTextData.text) || !enters) // adjust cursor only if we've changed some text or didn't process the enter workaround (this is to avoid when pressing just enter key, changes the cursor position on GBoard)
- {
- tl.cursorChanged(InputTextData.cur.y);
- tl._edit.sel =((InputTextData.cur.x==InputTextData.cur.y) ? -1 : InputTextData.cur.x);
- }
- _key_buffer_len=0; // this is a workaround for a bug in Google/Samsung Keyboard (but not SwiftKey) when Backspace key is triggered even though it shouldn't, when tapping Back key on the soft keyboard (when last character is space, or sometimes when just typed something), in that case 2 Back's are processed (one from EditText Java_com_esenthel_Native_text and one AINPUT_EVENT_TYPE_KEY), this code removes all queued keys to remove KB_BACK
- pass=tl.password();
- }break;
- }
- if(enters)Swap(temp, InputTextData);
- InputTextIs=false;
- }
- if(enters) // call after we got out of sync lock in case this method would trigger 'Java_com_esenthel_Native_text' and introduce some sort of deadlock
- {
- const Int scan_code=0; REP(enters){push(KB_ENTER, scan_code); queue('\n', scan_code); release(KB_ENTER);} _last_key_scan_code=-1; // manually push and release enter keys, use any scan_code>=0 to force linking characters with keys
- setTextInput(temp.text, temp.cur.x, temp.cur.y, pass);
- }
- }
- #endif
- // cursor visibility
- if(_cur>=0){_hidden=false; _curh_t=0;}else
- if((_curh_t+=Time.ad())>_curh_tn)
- {
- if(_curh_t>=2*_curh_tn)_curh_t =0;
- else _curh_t-=_curh_tn;
- _hidden^=1;
- }
- // misc
- _ctrl =FlagTest(_button[KB_LCTRL ]|_button[KB_RCTRL ], BS_ON|BS_PUSHED);
- _shift=FlagTest(_button[KB_LSHIFT]|_button[KB_RSHIFT], BS_ON|BS_PUSHED);
- _alt =FlagTest(_button[KB_LALT ]|_button[KB_RALT ], BS_ON|BS_PUSHED);
- _win =FlagTest(_button[KB_LWIN ]|_button[KB_RWIN ], BS_ON|BS_PUSHED);
- #if ANDROID
- KeySource=KEY_ANY; // re-allow input from all modes
- #endif
- if(b(KB_LALT) && !ctrl() && !win())
- {
- #if !WEB
- if(!(App.flag&APP_NO_CLOSE ) && bp(KB_F4 ) && !shift()){App.close(); eat(KB_F4 );} // simulate Alt+F4 behavior on (this is also needed on Windows)
- #endif
- if( (App.flag&APP_FULL_TOGGLE) && bp(KB_ENTER) ){D.toggle(shift()); eat(KB_ENTER);} // process Alt+Enter to toggle full-screen
- }
- _text_input=false;
- nextInQueue();
- }
- /******************************************************************************/
- // EAT
- /******************************************************************************
- do not advance the buffers, so remaining keys will be processed in the next frame, instead just clear current. This is because the code that checked for next key press was already executed in this frame, and because in each frame buffers are automatically advanced, then the key would disappear on next advance before the codes can check for it
- disable button flags (such as push), this is needed so when calling this method, it will prevent other Keyboard Shortcuts being triggered (those that are detected based on button flags)
- /******************************************************************************/
- void Keyboard::Key::eat()C
- {
- if(c==Kb.k.c)Kb.k.c='\0';
- if(k==Kb.k.k)
- {
- FlagDisable(Kb._button[k], BS_NOT_ON); // do this first, while 'k' is still available (before clearing, in case this is 'Kb.k')
- Kb.k.k=KB_NONE;
- }
- }
- void Keyboard::eat(Char8 c) {eat(Char8To16Fast(c));}
- void Keyboard::eat(Char c)
- {
- if(T.k(c) && c)
- {
- FlagDisable(_button[k.k], BS_NOT_ON); // do this first, while 'k' is still available
- k.clear();
- }
- }
- void Keyboard::eat(KB_KEY key)
- {
- FlagDisable(_button[key&0xFF], BS_NOT_ON); // always disable even if "T.k!=key"
- if(T.k(key) && key)k.clear();
- }
- void Keyboard::eatKey()
- {
- FlagDisable(_button[k.k], BS_NOT_ON); // do this first, while 'k' is still available
- k.clear();
- }
- void Keyboard::eat()
- {
- REPA(_button)FlagDisable(_button[i], BS_NOT_ON);
- k.clear();
- }
- /******************************************************************************/
- void Keyboard::nextInQueue()
- {
- if(!_key_buffer_len)k.clear();else
- {
- k=_key_buffer[_key_buffer_pos++];
- _key_buffer_len-- ;
- }
- }
- Keyboard::Key* Keyboard::nextKeyPtr()
- {
- return _key_buffer_len ? &_key_buffer[_key_buffer_pos] : null;
- }
- void Keyboard::nextKey()
- {
- //eatKey(); instead of calling this, just disable button state, because 'k' will be modified in 'nextInQueue'
- FlagDisable(_button[k.k], BS_NOT_ON); // do this first, while 'k' is still available
- nextInQueue();
- }
- void Keyboard::queue(C Key &key) // !! Warning: this doesn't check for 'key.k' and 'key.c' being invalid !!
- {
- if(_key_buffer_len<255)
- {
- _key_buffer[(_key_buffer_pos+_key_buffer_len)&0xFF]=key;
- _key_buffer_len++;
- _last_key_scan_code=-1;
- }
- }
- /******************************************************************************/
- void Keyboard::acquire(Bool on)
- {
- #if WINDOWS_OLD
- #if !KB_RAW_INPUT
- if(_did)
- {
- if(KEYBOARD_MODE==FOREGROUND) // we need to change acquire only if we're operating in Foreground mode
- {
- if(on)
- {
- _did->Acquire();
- // upon activating the app, we need to check if some keys are pressed, for some reason 'GetDeviceState' will not return it until the next frame
- if(GetKeyState(VK_LCONTROL)<0 && GetKeyState(VK_RMENU)>=0){push(KB_LCTRL ); _special|=1;} // we can enable LCTRL only if we know the right alt isn't pressed, because AltGr (Polish, Norwegian, .. keyboards) generates a false LCTRL
- if(GetKeyState(VK_LSHIFT )<0 ){push(KB_LSHIFT); _special|=2;}
- if(GetKeyState(VK_RSHIFT )<0 ){push(KB_RSHIFT); _special|=4;}
- }else _did->Unacquire();
- }else
- {
- // ignore recorded background input (this will remove keys from the DI buffer)
- DIDEVICEOBJECTDATA didod[BUF_KEYS];
- DWORD elms=BUF_KEYS, ret=_did->GetDeviceData(SIZE(DIDEVICEOBJECTDATA), didod, &elms, 0);
- }
- }
- #endif
- #endif
- #if WINDOWS || LINUX
- if(!on)REP(256)release(KB_KEY(i)); // need to manually release because Windows 'WM_*KEYUP', 'KeyUp' and Linux 'KeyRelease' aren't processed when app lost focus
- #endif
- }
- void Keyboard::exclusive(Bool on)
- {
- #if WINDOWS_OLD
- if(_exclusive!=on)
- {
- _exclusive=on; // set this first because it affects 'KEYBOARD_MODE'
- #if KB_RAW_INPUT
- if(App.hwnd())
- {
- RAWINPUTDEVICE rid[1];
- rid[0].usUsagePage=0x01;
- rid[0].usUsage =0x06; // keyboard
- rid[0].dwFlags =((KEYBOARD_MODE==BACKGROUND) ? RIDEV_INPUTSINK : 0)|(_exclusive ? RIDEV_NOHOTKEYS : 0);
- rid[0].hwndTarget =App.Hwnd();
- RegisterRawInputDevices(rid, Elms(rid), SIZE(RAWINPUTDEVICE));
- }
- #else
- if(_did)
- {
- _did->Unacquire(); // this also resets the 'GetDeviceState' of any currently pressed keys, they need to be pushed again to activate their state
- _did->SetCooperativeLevel(App.Hwnd(), (_exclusive ? DISCL_NOWINKEY : 0)|DISCL_NONEXCLUSIVE|((KEYBOARD_MODE==FOREGROUND) ? DISCL_FOREGROUND : DISCL_BACKGROUND));
- if(KEYBOARD_MODE==BACKGROUND || App.active())_did->Acquire(); // in background mode we always want the keyboard to be acquired, in foreground only if it's active
- // because calling 'Unacquire' resets the state, we need to remember if some keys are pressed, other keys don't have to be remembered because they are processed using WM_*KEY*
- _special=1*Kb.b(KB_LCTRL )
- |2*Kb.b(KB_LSHIFT)
- |4*Kb.b(KB_RSHIFT);
- }
- #endif
- }
- #endif
- }
- /******************************************************************************/
- Bool Keyboard::hwAvailable()
- {
- #if WINDOWS_NEW
- return Windows::Devices::Input::KeyboardCapabilities().KeyboardPresent>0;
- #elif DESKTOP
- return true;
- #elif ANDROID
- return (AndroidApp && AndroidApp->config) ? FlagTest(AConfiguration_getKeyboard(AndroidApp->config), (UInt)ACONFIGURATION_KEYBOARD_QWERTY) : false;
- // HW connected: AConfiguration_getKeyboard->2, AConfiguration_getKeysHidden->1
- // HW disconnected: AConfiguration_getKeyboard->1, AConfiguration_getKeysHidden->3
- #else
- return false;
- #endif
- }
- Bool Keyboard::softCoverage(Rect &rect)
- {
- if(_visible && !hwAvailable())
- {
- rect=D.pixelToScreen(T._recti);
- return true;
- }
- return false;
- }
- KB_KEY Keyboard::qwerty(KB_KEY qwerty)C {ASSERT(1<<(8*SIZE(qwerty))==ELMS(_qwerty)); return _qwerty[qwerty];}
- Bool Keyboard::visibleWanted()C {return Gui.kb() && (Gui.kb()->type()==GO_TEXTLINE || Gui.kb()->type()==GO_TEXTBOX) || _text_input;}
- /******************************************************************************/
- // KEYBOARD SHORTCUT
- /******************************************************************************/
- #if APPLE
- #define KBSC_CTRL_EX (KBSC_CTRL|KBSC_WIN_CTRL)
- #define KBSC_WIN_EX (KBSC_WIN |KBSC_CTRL_CMD)
- #else
- #define KBSC_CTRL_EX (KBSC_CTRL|KBSC_CTRL_CMD)
- #define KBSC_WIN_EX (KBSC_WIN |KBSC_WIN_CTRL)
- #endif
- inline Bool KbSc::testFlag()C
- {
- return FlagTest(flag, KBSC_CTRL_EX)==Kb.k.ctrl ()
- && FlagTest(flag, KBSC_SHIFT )==Kb.k.shift()
- && FlagTest(flag, KBSC_ALT )==Kb.k.alt ()
- && FlagTest(flag, KBSC_WIN_EX )==Kb.k.win ()
- && (flag& KBSC_REPEAT || Kb.k.first());
- }
- inline Bool KbSc::testFlagChar()C
- {
- return FlagTest(flag, KBSC_CTRL_EX)==Kb.k.ctrl ()
- //&& FlagTest(flag, KBSC_SHIFT )==Kb.k.shift() shift is not checked, because for KBSC_CHAR we just specify the character being lower/upper case, and KBSC_SHIFT would mess this up
- && FlagTest(flag, KBSC_ALT )==Kb.k.lalt () // only left Alt is checked, because right Alt may trigger accented characters
- && FlagTest(flag, KBSC_WIN_EX )==Kb.k.win ();
- }
- Bool KbSc::pd()C
- {
- switch(mode)
- {
- case KBSC_CHAR: return Kb.k.c==index && testFlagChar();
- case KBSC_KEY : return Kb.k.k==index && testFlag (); // must check either 'key' or 'bp', but not both, because 'key' is set to the first key queued in the buffer, if there are multiple keys then by default they will be processed one per frame, so if two keys were pressed, 2nd will be processed on the next frame, and in that case 'bp' for it is now false. We could check all keys in the buffer, however it's better to process keyboard shortcuts in order (for example if there's keyboard shortcuts: "Save" "Close" and user presses both very quickly in the same frame, we need to make sure that they are processed in correct order). 'key' was chosen instead of 'bp' to allow processing shortcuts in order
- }
- return false;
- }
- #if APPLE
- #define WIN_TEXT "Cmd+" // Apple keyboards have "Cmd" key instead of "Win" key
- #else
- #define WIN_TEXT "Win+"
- #endif
- Str KbSc::asText()C
- {
- Str s; switch(mode)
- {
- case KBSC_CHAR: {Char c=index; if(flag&KBSC_CTRL_EX)s+="Ctrl+"; if(flag&KBSC_WIN_EX)s+=WIN_TEXT; if(CharFlag(c)&CHARF_UP )s+="Shift+"; if(flag&KBSC_ALT)s+="Alt+"; if(CChar8 *n=CharName(c))s+=n;else s+=CaseUp(c);} break;
- case KBSC_KEY : { if(flag&KBSC_CTRL_EX)s+="Ctrl+"; if(flag&KBSC_WIN_EX)s+=WIN_TEXT; if( flag &KBSC_SHIFT)s+="Shift+"; if(flag&KBSC_ALT)s+="Alt+"; s+=Kb.keyName(KB_KEY(index)); } break;
- }
- return s;
- }
- void KbSc::eat()C
- {
- switch(mode)
- {
- case KBSC_CHAR: Kb.eat(Char (index)); break;
- case KBSC_KEY : Kb.eat(KB_KEY(index)); break;
- }
- }
- /******************************************************************************/
- }
- /******************************************************************************/
- #if ANDROID
- extern "C"
- {
- JNIEXPORT void JNICALL Java_com_esenthel_Native_text(JNIEnv *env, jclass clazz, jstring text, jint start, jint end)
- {
- JNI jni(env);
- SyncLocker locker(InputTextLock);
- InputTextData.text=jni(text);
- InputTextData.cur .set(start, end);
- InputTextIs=true;
- }
- JNIEXPORT void JNICALL Java_com_esenthel_Native_key(JNIEnv *env, jclass clazz, jint chr, jint key_code)
- {
- switch(KeySource)
- {
- case KEY_ANY : KeySource=KEY_JAVA; // !! no break on purpose !!
- case KEY_JAVA: Kb.queue(Char(chr), key_code); break;
- }
- }
- }
- #endif
- /******************************************************************************/
|