Native keyboard issue on Android

Help
2013-10-14
2013-10-17
  • Hi and congratulations for the great project!
    I'm connecting to a remote desktop via RDP and I've got a strange issue on Android: using the native keyboard of my device, it seems that only numbers are correctly transmitted to the RD, no letters, no symbols.
    Any clues?
    Ruggero

     
  • Hi
    After further investigation I found out that the root of this issue could be related to this: http://stackoverflow.com/questions/15476079/onkeydown-is-not-invoked-while-using-soft-keypads

    The Guacamole implementation uses a textarea input field to trigger the device's soft keyboard behavior and input.

    Due to the missing keyboard events, the only possible way to work around the problem seems to observe the content of the input field that's been edited. The techniques to achieve this are various and have pros and cons.

    One way could be to set a timer and keep comparing the text of the textarea with the value of a variable, when the two values are different, send a keydown+keyup for each new character or a backspace for any missing one and finally update the value of the variable with the content of the input field...

    Another one is to register a handler to the oninput event in addition to the already registered handlers to the onkeydown, onkeypress and onkeyup events and use a comparison method like the one described above in order to send the keystrokes down to the Guacamole tunnel.

    I preferred the latter, even if the implementation is not perfect, due the weird/buggy implementation of the oninput event in some browsers. According to my tests it works very well with Firefox for Android, instead on Chrome, Opera and the native Android browser sometimes a key is missed, nevertheless in general it doesn't bother too much.

    I thought to post the changes that need to be performed to the keyboard.js, in order to implement the described workaround. Hope it helps and that it could be used as a starting point to be improved in future releases.

    Code changes, valid for the present version v. 0.8.3, in keyboard.js:
    1. comment out the delegate method element.addEventListener("keypress", function(e)...
    2. add the following two lines:
    element.addEventListener("keypress", keypressHandler, true);
    element.addEventListener("input", keypressHandler, true);
    3. add the global variable and the new delegate function:
    var _keyboardTargetValue = "";
    function keypressHandler(e) {

        // Only intercept if handler set
        if (!guac_keyboard.onkeydown && !guac_keyboard.onkeyup) return;
    
        var keynum;
        if (window.event) keynum = window.event.keyCode;
        else if (e.which) keynum = e.which;
    
        // manages the oninput event
        if (keynum == null) {
            if (e.target.value.length == 0 && _keyboardTargetValue.length > 1) {
                // missed key... nothing must happen
                e.target.value = _keyboardTargetValue;
            } else {
                if (_keyboardTargetValue.length != e.target.value.length) {
                    try {
                        if (_keyboardTargetValue.length - e.target.value.length == 1) {
                            // the resulting length of the text in the textarea is lower, guess a backspace
                            keynum = 8;
                        } else if (_keyboardTargetValue.length - e.target.value.length == -1) {
                            // determines the pressed key by the value of the new character
                            keynum = e.target.value.substring(_keyboardTargetValue.length).charCodeAt(0);
                        } else {
                            // there's a mismatch between the value of the target textarea and the event
                            // probably because of buggy event handling by the browser
                            // in this case force the value of the event with the current value of the textarea
                            e.target.value = _keyboardTargetValue;
                        }
                    } catch (Exception) {
                    } finally {
                        _keyboardTargetValue = e.target.value;
                    }
                }
            }
            if (keynum == null) return;
        }
    
        e.preventDefault();
    
        var keysym = keysym_from_charcode(keynum);
    
        // Fix modifier states
        update_modifier_state(e);
    
        // If event identified as a typable character, and we're holding Ctrl+Alt,
        // assume Ctrl+Alt is actually AltGr, and release both.
        if (!isControlCharacter(keynum) && guac_keyboard.modifiers.ctrl && guac_keyboard.modifiers.alt) {
            release_key(0xFFE3); // Left ctrl
            release_key(0xFFE4); // Right ctrl
            release_key(0xFFE9); // Left alt
            release_key(0xFFEA); // Right alt
        }
    
        // Send press + release if keysym known
        if (keysym != null) {
            press_key(keysym);
            release_key(keysym);
        }
    }
    
    1. add the following lines at the bottom of the delegate function: element.addEventListener("keyup", function(e)...
      // in case of regular keyup event, updates the value of the variable
      // with the actual text in the target textarea
      var keyboardTargets = document.getElementsByTagName('textarea');
      if (keyboardTargets.length > 0)
      _keyboardTargetValue = keyboardTargets[0].value;

    Happy typing on Android soft keyboards :-)