Xpra: Ticket #2547: Dead keys not working in html5 client

Dead keys circumflex, backtick, tick are not working in the html5 client for a German keyboard.

Short summary: I access xpra from Windows Chrome. I press the dead key circumflex, which is recognized as a NumLock / ssharp / NumLock event. Doing the same in x2go, the key gives event dead_circumflex.

Unfortunately, the bug report tool is not working for me in the html client, I'm trying to include all relevant information for the circumflex case:

Server version v3.0.5 on Ubuntu 18.04 keyboard layout German (de)

Client is Chrome, current version, on Windows

Runing in the xpra client:

wuebbel@xpra:~$ setxkbmap -query
rules:      evdev
model:      evdev
layout:     de
wuebbel@xpra:~$ setxkbmap -print
xkb_keymap {
	xkb_keycodes  { include "evdev+aliases(qwertz)"	};
	xkb_types     { include "complete"	};
	xkb_compat    { include "complete"	};
	xkb_symbols   { include "pc+de+inet(evdev)"	};
	xkb_geometry  { include "pc(pc104)"	};
};

Running in x2go on the same Windows machine (where the dead keys work):

CBM8032% setxkbmap -print
xkb_keymap {
	xkb_keycodes  { include "xfree86+aliases(qwertz)"	};
	xkb_types     { include "complete"	};
	xkb_compat    { include "complete+xfree86(grab_break)"	};
	xkb_symbols   { include "pc+de+inet(pc105)"	};
	xkb_geometry  { include "pc(pc105)"	};
};
CBM8032% setxkbmap -query
rules:      xorg
model:      pc105
layout:     de
options:    grab:break_actions

Debug code in the Browser Console:

keyboard processKeyEvent( false ,  KeyboardEvent, key= Backquote keycode= 220
KeyboardEvent {isTrusted: true, key: "Dead", code: "Backquote", location: 0, ctrlKey: false, …}
isTrusted: true
key: "Dead"
code: "Backquote"
location: 0
ctrlKey: false
shiftKey: false
altKey: false
metaKey: false
repeat: false
isComposing: false
charCode: 0
keyCode: 220
view: Window {parent: Window, opener: null, top: Window, length: 0, frames: Window, …}
detail: 0
sourceCapabilities: InputDeviceCapabilities {firesTouchEvents: false}
which: 220
type: "keyup"
target: textarea#pasteboard
currentTarget: null
eventPhase: 0
bubbles: true
cancelable: true
defaultPrevented: true
composed: true
timeStamp: 3879787.2299999
srcElement: textarea#pasteboard
returnValue: false
cancelBubble: false
path: (6) [textarea#pasteboard, div, body, html, document, Window]
__proto__: KeyboardEvent
​

xpra server debug output for keyboard:

2020-01-12 17:31:01,444 set_keyboard_layout_group(0) config=KeyboardConfig(de / None / None), current keyboard group=0
2020-01-12 17:31:01,444 get_keycode(220, backslash, ('mod2',))=20 (level=0)
2020-01-12 17:31:01,444 process_key_action([b'key-action', 14, b'backslash', 1, (b'mod2',), 220, b'dead', 220, 0]) server keycode=20
2020-01-12 17:31:01,445 make_keymask_match(('mod2',)) current mask: set(), wanted: {'mod2'}, ignoring=20/['backslash'], keys_pressed={}
2020-01-12 17:31:01,445 change_mask(set(), False, remove) failed=[]
2020-01-12 17:31:01,445 keynames(mod2)={'Num_Lock'}, keycodes=[77], nuisance=True, nuisance keys={'mod2', 'lock'}
2020-01-12 17:31:01,445 change_mask(add) ('mod2',) modifier 'mod2' using keycode 77
2020-01-12 17:31:01,446 change_mask({'mod2'}, True, add) failed=[]
2020-01-12 17:31:01,446 is_modifier(20) not found
2020-01-12 17:31:01,446 handle_key((14, 1, 'backslash', 220, 20, ('mod2',), False, True))
2020-01-12 17:31:01,446 handle keycode pressing    20: key 'backslash'
2020-01-12 17:31:01,446 fake_key(20, True)
2020-01-12 17:31:01,596 set_keyboard_layout_group(0) config=KeyboardConfig(de / None / None), current keyboard group=0
2020-01-12 17:31:01,596 process_key_action([b'key-action', 14, b'backslash', 0, (b'mod2',), 220, b'dead', 220, 0]) server keycode=20
2020-01-12 17:31:01,597 is_modifier(20) not found
2020-01-12 17:31:01,597 handle_key((14, 0, 'backslash', 220, 20, ('mod2',), False, True))
2020-01-12 17:31:01,597 handle keycode unpressing  20: key 'backslash'
2020-01-12 17:31:01,597 fake_key(20, False)
2020-01-12 17:31:05,203 make_keymask_match(()) current mask: {'mod2'}, wanted: set(), ignoring=None/None, keys_pressed={}
2020-01-12 17:31:05,203 keynames(mod2)={'Num_Lock'}, keycodes=[77], nuisance=True, nuisance keys={'mod2', 'lock'}
2020-01-12 17:31:05,204 change_mask(remove) () modifier 'mod2' using keycode 77
2020-01-12 17:31:05,204 change_mask({'mod2'}, False, remove) failed=[]
2020-01-12 17:31:05,204 change_mask(set(), True, add) failed=[]

xev output:

KeyPress event, serial 36, synthetic NO, window 0x600001,
    root 0x299, subw 0x0, time 1758323680, (1112,-93), root:(1122,3),
    state 0x0, keycode 77 (keysym 0xff7f, Num_Lock), same_screen YES,
    XLookupString gives 0 bytes:
    XmbLookupString gives 0 bytes:
    XFilterEvent returns: False
KeyRelease event, serial 36, synthetic NO, window 0x600001,
    root 0x299, subw 0x0, time 1758323680, (1112,-93), root:(1122,3),
    state 0x10, keycode 77 (keysym 0xff7f, Num_Lock), same_screen YES,
    XLookupString gives 0 bytes:
    XFilterEvent returns: False
KeyPress event, serial 36, synthetic NO, window 0x600001,
    root 0x299, subw 0x0, time 1758323681, (1112,-93), root:(1122,3),
    state 0x10, keycode 20 (keysym 0xdf, ssharp), same_screen YES,
    XLookupString gives 1 bytes: (df) "ß"
    XmbLookupString gives 1 bytes: (df) "ß"
    XFilterEvent returns: False
KeyRelease event, serial 36, synthetic NO, window 0x600001,
    root 0x299, subw 0x0, time 1758323833, (1112,-93), root:(1122,3),
    state 0x10, keycode 20 (keysym 0xdf, ssharp), same_screen YES,
    XLookupString gives 1 bytes: (df) "ß"
    XFilterEvent returns: False
KeyPress event, serial 36, synthetic NO, window 0x600001,
    root 0x299, subw 0x0, time 1758327439, (287,-96), root:(297,0),
    state 0x10, keycode 77 (keysym 0xff7f, Num_Lock), same_screen YES,
    XLookupString gives 0 bytes:
    XmbLookupString gives 0 bytes:
    XFilterEvent returns: False
KeyRelease event, serial 36, synthetic NO, window 0x600001,
    root 0x299, subw 0x0, time 1758327439, (287,-96), root:(297,0),
    state 0x10, keycode 77 (keysym 0xff7f, Num_Lock), same_screen YES,
    XLookupString gives 0 bytes:
    XFilterEvent returns: False

xev on x2go:

KeyPress event, serial 28, synthetic NO, window 0x800001,
    root 0xf8, subw 0x0, time 2597785113, (152,247), root:(288,406),
    state 0x0, keycode 49 (keysym 0xfe52, dead_circumflex), same_screen YES,
    XLookupString gives 1 bytes: (5e) "^"
    XmbLookupString gives 0 bytes:
    XFilterEvent returns: True
KeyRelease event, serial 31, synthetic NO, window 0x800001,
    root 0xf8, subw 0x0, time 2597785222, (152,247), root:(288,406),
    state 0x0, keycode 49 (keysym 0xfe52, dead_circumflex), same_screen YES,
    XLookupString gives 1 bytes: (5e) "^"
    XFilterEvent returns: False


Mon, 13 Jan 2020 07:00:52 GMT - Antoine Martin: attachment set

German T2 keyboard layout


Mon, 13 Jan 2020 07:03:38 GMT - Antoine Martin: status, description changed

@wuebbel: I assume that the keyboard layout looks like this one: German T2 keyboard layout And that the key you are pressing is the one on the top-left? (left of "1")

Does the server correctly set a 'de' keymap when the html5 client connects? Please attach the output of xpra info with the html5 client is connected.


Mon, 13 Jan 2020 08:10:32 GMT - Frank Wuebbeling: attachment set

xpra info output


Mon, 13 Jan 2020 08:11:15 GMT - Frank Wuebbeling:

Yes, that's the keyboard layout

I manually chose German in the HTML5 client. In the console log, the client notes that keyboard ="de". Also, the setxkbmap output attached above shows layout="de". a umlaut etc. are working as expected.

I am just now running from a different client. The output of xev inside xpra is slightly different: The "NumLock" event is gone, and circumflex just produces an ssharp keysym.

I attach xpra info output.


Tue, 14 Jan 2020 04:03:23 GMT - Antoine Martin: attachment set

changes the key names for some 'de' keycodes


Tue, 14 Jan 2020 04:06:12 GMT - Antoine Martin:

I am not sure how we're supposed to know that this key should be mapped to dead_circumflex:

isTrusted: true
key: "Dead"
code: "Backquote"
location: 0
charCode: 0
keyCode: 192

Can also be seen using this useful tool: https://keycode.info/.

So, the patch above changes the keycode definition for two keys: dead_circumflex and backtick. Works for me.

I couldn't find the tick key you were referring to. I tried Shift+7 and got the forward slash I was expecting.

I am not applying this patch just yet, because I want to figure out if this change can be applied as-is or if it should be specific to the 'de' layout, as this may break other layouts otherwise.

See: keyCode property of key events: The following punctuation characters may change virtual codes between keyboard layouts. KeyboardEvent Value (keyCodes, metaKey, etc)


Tue, 14 Jan 2020 06:37:03 GMT - Frank Wuebbeling:

The key that is also not working is the key left to backspace (should be producing dead_acute, dead_grave with shift, but gives "9").

I wanted to apply your patch, but was surprised: I do have your 3.0.5 installed (Ubuntu):

xpra-html5/bionic,now 3.0.5-r24939-1 amd64 [installed]

but the file Keycodes.js looks very much different from the file in your repository. In fact, it ends with

CHARCODE_TO_NAME[111+i]="F"+i;

Could it be that on Ubuntu an old version is installed? And that this is the real problem?


Tue, 14 Jan 2020 08:11:48 GMT - Antoine Martin:

The key that is also not working is the key left to backspace (should be producing dead_acute, dead_grave with shift, but gives "9").

Just add:

CHARCODE_TO_NAME[187] = "dead_acute"

Since https://keycode.info/ shows 187 when I press this key with a 'de' layout.

but the file Keycodes.js looks very much different from the file in your repository. In fact, it ends with

The version you find in the package has been minified, which re-orders some functions. Nothing to worry about. FYI: you should also delete the .br and .gz versions of that file (those are optional).


Tue, 14 Jan 2020 19:01:28 GMT - Frank Wuebbeling:

Antoine, I now got it to work. Pointing at my original post:

keycode= 220

for the circumflex. So I had to slightly modify your patch:

CHARCODE_TO_NAME[221] = "dead_acute"
CHARCODE_TO_NAME[220] = "dead_circumflex"

Again, I'm surprised. What I'm doing here seems to be exactly what is already present in the logfile https://xpra.org/trac/raw-attachment/ticket/684/server.log which has been posted here some time before. It contains a line 221 - dead_acute... 220 - dead_circumflex... But with several more definitions...

However, making the change, xev now shows the correct keys. I'm going to try everything in my main installation tomorrow, try all keys and report back.

Great! Seems to do the trick for me at this point.


Wed, 15 Jan 2020 18:25:05 GMT - Frank Wuebbeling: status changed; resolution set

Ok, I've tried. The patch fixes the original problem, so I mark this bug as fixed (if I have the permission). I changed charcodes 220 and 221 to dead_circumflex and dead_acute, resp. (see last post). I am not sure whether this will break non-german keyboards.

What is currently not working for me, but I don't care since I don't use these keys:

Keypad keys do not return KP_XXX as I would expect, but rather their main-keyboard equivalent, so KP_Add is detected as plus eg. KP_Divide is mapped to key 7.

Everything else is working now for me. Thanks!


Thu, 16 Jan 2020 04:26:58 GMT - Antoine Martin: status changed; resolution deleted

Re-opening until the fix is committed.


Wed, 22 Jan 2020 07:35:31 GMT - Antoine Martin: status changed; resolution set

Commit in r25034. (will backport)

So far, no problems during testing with us, gb and fr layouts. (tested Chrome and Firefox on Linux and mswindows)


Thu, 21 May 2020 04:41:43 GMT - Antoine Martin:

See also #2769.


Sat, 23 Jan 2021 05:54:13 GMT - migration script:

this ticket has been moved to: https://github.com/Xpra-org/xpra/issues/2547