TranslateKey is completely messed up

 

I've been doing some tests translating keyboard events to a string (kind of what you would expect any edit field to do, either in metatrader, or your web browser, or anywhere). Nothing too crazy at first, you press 'a', TranslateKey returns 'a' (or 'A' if shift is pressed). But when dealing with dead keys (I have a spanish keyboard) things get quite a bit crazier...

First test, I noticed that dead keys modifiers weren't applying to the following keys (for example, "`"+"a" should give "à")

void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam)
{
        if (id!=CHARTEVENT_KEYDOWN) return;
        Print(lparam, " ", ShortToString(TranslateKey((int)lparam)));
}
//RESULT (after pressing some keys)
//65 a
//186 `
//65 a
//16 
//186 ^
//65 A

The dead key returns its symbol, and doesn't get applied to the following letter.

However, if you do both translatekeys in a row, the result is correct. Let's say I stored somehow that dead key (186) and applied it before:

void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam)
{
        if (id!=CHARTEVENT_KEYDOWN) return;
        if (lparam==16) return; //Shift
        Print(TranslateKey(186));
        Print(lparam," ", ShortToString(TranslateKey((int)lparam)));
        
        //And this below would result in something different than above (maybe expected)
        //Print(lparam," ",TranslateKey(186)," ", ShortToString(TranslateKey((int)lparam)));
}
//RESULT
//-1
//65 à
//-1
//69 Ê

Now the dead key returns -1 (as expected, since it doesn't return anything) and then the letter I pressed with its symbol combined. However, if I press shift while typing the letter, I get a different symbol (^). In this example, it wouldn't be possible to have "â" or "È" for instance (well, I could use caps lock probably).

Now one last case, translating only a letter ("a") whenever a keydown event happens:

void OnChartEvent(const int id,const long& lparam,const double& dparam,const string& sparam)
{
        if (id!=CHARTEVENT_KEYDOWN) return;
        if (lparam==16) return; //Shift
        Print(lparam," ", ShortToString(TranslateKey('A')));
}
//RESULT
//65 a
//186 à
//186 Â

Now they also get combined "correctly", but with the same problem when pressing shift. Moreover, in some other tests I did before (which I can't remember how to replicate reliably) the function didn't even get correctly whether shift was being pressed or not! (No idea why).

If I can get a conclusion for this, is that (I think, just from viewing from outside) when I press a key, ToUnicodeEx (the internal function that also gets called in translatekeys) is executed, and when calling TranslateKeys it gets called again, messing up all dead keys (since when pressing them twice, two characters get written: "``"). I don't know if it's a microsoft problem or a metaquotes one (or how much of it is because of each one), but from what I found here in stackoverflow it seems that they should call ToUnicodeEx in a different thread (however that is done). Should I expect this to ever get fixed? Or is it not even worth their time for such a specific function?

Lastly, another fun effect... if you run this EA/script, next key you press anywhere (browser, metaeditor, notepad...) would spit 2 characters or the combination of them that you wouldn't expect normally

int OnInit()
{
   TranslateKey(186);
   return(INIT_SUCCEEDED);
}

For people with english (US INTL) keyboards, you can use 192 as an example of a dead key (which is "`" or "~" with shift)

ToAscii/ToUnicode in a keyboard hook destroys dead keys
ToAscii/ToUnicode in a keyboard hook destroys dead keys
  • 2009.12.26
  • 00010000
  • stackoverflow.com
It seems that if you call or while in a global WH_KEYBOARD_LL hook, and a dead-key is pressed, it will be 'destroyed'. For example, say you've configured your input language in Windows as Spanish, and you want to type an accented letter á in a program. Normally, you'd press the single-quote key (the dead key), then the letter "a", and then on...
 
(Machine translation)

From build 4240, changes have been made to keypress processing

  1. Added CHARTEVENT_KEYUP event to OnChartEvent

  2. The behavior of TranslateKey and TetminalInfoInteger functions depends on call context

    Let's introduce the concept of KeyEvent - if the keyboard event CHARTEVENT_KEYUP or CHARTEVENT_KEYDOWN was received in OnChartEvent. Now:

    • When calling TetminalInfoInteger or TranslateKey from a KeyEvent , these functions use the keyboard state cached at the time the keyboard event occurred.
      This is done for holistic (consistent) processing of keys, for example, the state of the SHIFT, CTRL keys, etc. Since at the time the keyboard event occurred, the MQL program could be busy processing another event.

    • in other cases, namely when calling TetminalInfoInteger or TranslateKey outside KeyEvent , the current state of the keyboard is returned.

  3. THERE IS A LIMIT ! If you create windows or dialogs from an MQL program using CreateWindow or CreateDialog (does not apply to the MessageBox/FileSelectDialog functions), then calling TetminalInfoInteger or TranslateKey for KeyEvent will not return the current state of the keyboard, but will return data cached by the Windows system at the time the created window was closed.

    If you create windows from DLLs (via imported functions), then the windows should be created in a separate thread.
    This is due to the peculiarities of the Windows OS.

  4. Added handling of DEAD ΚΕΥ, keys that change the next letter typed,

    For example, for the Greek alphabet, the following will become available through TranslateKey: ά έ ύ, etc.


void OnChartEvent(const int id, const long &lparam, const double&, const string&)
  {
   if(id==CHARTEVENT_KEYDOWN || id==CHARTEVENT_KEYUP)  
     {
      short symbol=TranslateKey((int)lparam);
      
      if(symbol>0)
        {
         if(symbol==27)
            ExpertRemove();
         else
            PrintFormat("%s: '%s'",id==CHARTEVENT_KEYDOWN ? "DOWN" : "UP",ShortToString(symbol));
        }
     }
  }
output:
2024.03.19 16:39:51.459	Test (EURUSD,H1)	DOWN: 'ά'
2024.03.19 16:39:51.539	Test (EURUSD,H1)	UP: 'α'
! But, only DOWN KEY event gives right result
 

I've tested this weekend the results of TranslateKey, and while it now works correctly for all base cases, dead keys that are accessed with shift or altGr don't work yet.

I'm using the spanish keyboard (which has both cases, I don't know if there are other types of dead keys in other keyboard layouts, and I don't know if the greek layout has the cases I'm talking about)

From wikipedia:


With this layout, for instance (correct if it gets the same result in mt5):

  • "´" + "a" = "á" (correct)
  • shift("´")/"¨" + "a" = ä (incorrect)
  • altgr("4")/"~" + "a" = ã (incorrect)
  • "´" + shift("a")/"A" = "Á" (correct)
  • shift("´")/"¨" + shift("a")/"A" = Ä (incorrect)
  • altgr("4")/"~" + shift("a")/"A" = Ã (incorrect)

void OnChartEvent(const int id, const long &lparam, const double&, const string&)
  {
   if(id==CHARTEVENT_KEYDOWN /*|| id==CHARTEVENT_KEYUP*/)  
     {
      short symbol=TranslateKey((int)lparam);
      int shift = TerminalInfoInteger(TERMINAL_KEYSTATE_SHIFT);
      int alt = TerminalInfoInteger(TERMINAL_KEYSTATE_MENU);
      int ctrl = TerminalInfoInteger(TERMINAL_KEYSTATE_CONTROL);
      
      if (lparam >= 16 && lparam <=18) return; //Shift, ctrl, alt (alt Gr = alt+ctrl)
      
      PrintFormat("key=%d: '%s' (Shift: %x; Alt: %x; Ctrl: %x)",lparam,ShortToString(symbol),shift,alt,ctrl);
     }
  }
2024.03.23 11:21:31.767 TestKeys (EURUSD,D1)    key=65: 'a' (Shift: 0; Alt: 0; Ctrl: 0)
2024.03.23 11:21:32.916 TestKeys (EURUSD,D1)    key=52: '4' (Shift: 0; Alt: 0; Ctrl: 0)
2024.03.23 11:21:34.564 TestKeys (EURUSD,D1)    key=50: '@' (Shift: 0; Alt: ffffff80; Ctrl: ffffff80)

2024.03.23 11:21:36.358 TestKeys (EURUSD,D1)    key=222: '￿' (Shift: 0; Alt: 0; Ctrl: 0)
2024.03.23 11:21:37.021 TestKeys (EURUSD,D1)    key=65: 'á' (Shift: 0; Alt: 0; Ctrl: 0)

2024.03.23 11:21:39.718 TestKeys (EURUSD,D1)    key=222: '￿' (Shift: ffffff80; Alt: 0; Ctrl: 0)
2024.03.23 11:21:40.377 TestKeys (EURUSD,D1)    key=65: 'á' (Shift: 0; Alt: 0; Ctrl: 0)         //-> 'ä'

2024.03.23 11:21:43.220 TestKeys (EURUSD,D1)    key=52: '￿' (Shift: 0; Alt: ffffff80; Ctrl: ffffff80)
2024.03.23 11:21:44.731 TestKeys (EURUSD,D1)    key=65: 'a' (Shift: 0; Alt: 0; Ctrl: 0)         //-> 'ã'

2024.03.23 11:21:59.509 TestKeys (EURUSD,D1)    key=222: '￿' (Shift: ffffff80; Alt: 0; Ctrl: 0)
2024.03.23 11:22:00.183 TestKeys (EURUSD,D1)    key=65: 'Á' (Shift: ffffff80; Alt: 0; Ctrl: 0)  //-> 'Ä'

2024.03.23 11:22:10.740 TestKeys (EURUSD,D1)    key=222: '￿' (Shift: 0; Alt: 0; Ctrl: 0)
2024.03.23 11:22:11.731 TestKeys (EURUSD,D1)    key=65: 'Á' (Shift: ffffff80; Alt: 0; Ctrl: 0)

I used this website to make some tests: https://www.toptal.com/developers/keycode

There is also the issue about alt codes for other unicode characters (alt + several keypad numbers) which I don't know how is handled or if it could be considered (it doesn't even work in that webpage I used) to get access to any unicode character. It should be noted that key events don't trigger while using Alt (neither "alt" itself), maybe because some shortcuts in MT5 are "Alt+some key". Alt codes do work in any basic text input though (browser search, metaeditor or a default edit object)


Also I have to say, I didn't even expect KEYUP events to be added, but that was a very nice update!

Reason: