Xpra: Ticket #230: allow the user to switch keyboard layout manually at runtime

Here is a very strange problem:

When I connect to xpra session from local machine (the very one where xpra server is started) sometimes keyboard accidentally switches to national layout in a way that it is impossible to switch keyboard back to English. For example I was typing an email in English when suddenly without pressing modifier combination (Alt+Shift) keyboard switched to national layout for all applications in session. I couldn't switch back to English at all. I tried re-connecting and "xpra upgrade" but nothing helped. The following error was logged:

2013-01-04 01:05:11,801 setting key repeat rate from client: 492ms delay / 50ms interval
2013-01-04 01:05:11,803 setting keymap: {'rules': 'evdev', 'model': 'pc105', 'layout': 'us'}
2013-01-04 01:05:11,838 setting full keymap definition from client via xkbcomp
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/xpra/server.py", line 1172, in _process_server_settings
    self.update_server_settings(packet[1])
  File "/usr/lib/python2.7/dist-packages/xpra/server.py", line 1209, in update_server_settings
    self._xsettings_manager = XSettingsManager(v)
  File "/usr/lib/python2.7/dist-packages/xpra/xposix/xsettings.py", line 34, in __init__
    self._set_blob_in_place(settings_blob)
  File "/usr/lib/python2.7/dist-packages/xpra/xposix/xsettings.py", line 40, in _set_blob_in_place
    settings_blob)
  File "/usr/lib/python2.7/dist-packages/wimpiggy/prop.py", line 293, in prop_set
    _prop_encode(target, etype, value))
  File "/usr/lib/python2.7/dist-packages/wimpiggy/prop.py", line 274, in _prop_encode
    return _prop_encode_scalar(disp, etype, value)
  File "/usr/lib/python2.7/dist-packages/wimpiggy/prop.py", line 279, in _prop_encode_scalar
    return (atom, formatbits, serialize(disp, value))
  File "/usr/lib/python2.7/dist-packages/wimpiggy/xsettings_prop.py", line 95, in set_settings
    assert type(value)==int
AssertionError

The above is from Xpra 0.7.6. There were no clipboard manager(s) running and I was typing in kmail when it happened but I have a feeling that application doesn't matter because I remember it happened before in some other apps.

Interestingly enough only local connection is affected. I was able to switch layout back to English when I connected to session from another computer over SSH.

Please advise what kind of debug log may be useful. Thanks.



Thu, 03 Jan 2013 16:07:00 GMT - Antoine Martin: status changed

First, are you sure that the stacktrace above is related to the problem you experienced? This stacktrace seems to have been recorded during the connection setup, and your problem only occurred afterwards didn't it? XSETTTINGS aren't related to the keyboard state at all. Second, what version was the client, also 0.7.6? What desktop environment, etc? I've reviewed the code that prepares the server settings (and it is very easy to read - wasn't easy to write for sure..), and the only way one can get a XSettingsTypeInteger value is from value = struct.unpack("=I", d[pos:pos+4])[0] - I don't see how it is ever possible to get anything but an integer from this call. Nonetheless, I've added much more thorough validation of the data both on the way out (extracting XSETTINGS from the client - see r2444) and on the way in (updating the server - see r2443). Please use trunk or apply to 0.7.6 (it will apply cleanly as this class has not changed much since ~0.4 - the whole thing is just broken in 0.3 and older) If you can reproduce this, it would really help to get the debug output (xpra -d all) from both the client and server.


Now, the layout issue is probably something else. Are you using keyboard sync mode or not? Can you reproduce this with debug switched on? I think it is possible that as you move around and focus/unfocus windows, we press modifier keys for you on the server side to make sure the state of the server keyboard matches your client's state... and we may end up triggering this combination accidentally. That's unfortunate, and also hard to fix. I can't think of an easy way around that. A workaround would be to use keys which are not all modifiers for layout switching, as we never press those automagically.


Fri, 04 Jan 2013 11:35:01 GMT - onlyjob:

Replying to antoine:

First, are you sure that the stacktrace above is related to the problem you experienced?

No I'm not sure. However just like the keyboard problem I see this error only with local connection so I thought it might be related.

Second, what version was the client, also 0.7.6? What desktop environment, etc?

Yes 0.7.6 both server and client (remember it's local connection ;)

I've reviewed the code that prepares the server settings (and it is very easy to read - wasn't easy to write for sure..), and the only way one can get a XSettingsTypeInteger value is from value = struct.unpack("=I", d[pos:pos+4])[0] - I don't see how it is ever possible to get anything but an integer from this call. Nonetheless, I've added much more thorough validation of the data both on the way out (extracting XSETTINGS from the client - see r2444) and on the way in (updating the server - see r2443). Please use trunk or apply to 0.7.6 (it will apply cleanly as this class has not changed much since ~0.4 - the whole thing is just broken in 0.3 and older) If you can reproduce this, it would really help to get the debug output (xpra -d all) from both the client and server.

Understood, will try as soon as I can.


Now, the layout issue is probably something else. Are you using keyboard sync mode or not?

No keyboard synch. (sorry, forgot to mention).

Can you reproduce this with debug switched on? I think it is possible that as you move around and focus/unfocus windows, we press modifier keys for you on the server side to make sure the state of the server keyboard matches your client's state... and we may end up triggering this combination accidentally. That's unfortunate, and also hard to fix. I can't think of an easy way around that. A workaround would be to use keys which are not all modifiers for layout switching, as we never press those automagically.

I see... Also I didn't try with keyboard sync yet... Will post more info. when I have it. Cheers.


Sun, 06 Jan 2013 04:45:34 GMT - onlyjob:

Just a little update: I tried r2443 and r2444 changesets applied to 0.7.6 with same effect. Here is log fragment taken with "-d all":

2013-01-06 15:30:29,301   not forwarding to CompositeHelper handler, it has no wimpiggy-property-notify-event signal
2013-01-06 15:30:29,302 grok_modifier_map(<gtk.gdk.Display object at 0x15804b0 (GdkDisplayX11 at 0x1615230)>,{'ISO_Level3_Shift': 'mod5', 'Mode_switch': 'mod5', 'Meta_L': 'mod1', 'Control_R': 'control', 'Super_R': 'mod4', 'Alt_R': 'mod1', 'Hyper_L': 'mod4', 'Caps_Lock': 'lock', 'Alt_L': 'mod1', 'Num_Lock': 'mod2', 'Super_L': 'mod4', 'Shift_R': 'shift', 'Shift_L': 'shift', 'Control_L': 'control'})={'control': 4, 'hyper': 0, 'lock': 2, 'meta': 0, 'alt': 0, 'super': 0, 'nuisance': 2, 'mod1': 8, 'mod2': 16, 'mod3': 32, 'mod4': 64, 'mod5': 128, 'shift': 1, 'num': 0, 'scroll': 0}
2013-01-06 15:30:29,302 modifier_map({'ISO_Level3_Shift': 'mod5', 'Mode_switch': 'mod5', 'Meta_L': 'mod1', 'Control_R': 'control', 'Super_R': 'mod4', 'Alt_R': 'mod1', 'Hyper_L': 'mod4', 'Caps_Lock': 'lock', 'Alt_L': 'mod1', 'Num_Lock': 'mod2', 'Super_L': 'mod4', 'Shift_R': 'shift', 'Shift_L': 'shift', 'Control_L': 'control'})={'control': 4, 'hyper': 0, 'lock': 2, 'meta': 0, 'alt': 0, 'super': 0, 'nuisance': 2, 'mod1': 8, 'mod2': 16, 'mod3': 32, 'mod4': 64, 'mod5': 128, 'shift': 1, 'num': 0, 'scroll': 0}
2013-01-06 15:30:29,302 compute_modifier_keynames: keycodes_for_modifier_keynames={'ISO_Level3_Shift': set([92]), 'Mode_switch': set([203]), 'Meta_L': set([205]), 'Control_R': set([105]), 'Super_R': set([134]), 'Alt_R': set([108]), 'Hyper_L': set([207]), 'Caps_Lock': set([66]), 'Alt_L': set([64, 204]), 'Num_Lock': set([77]), 'Super_L': set([133, 206]), 'Shift_R': set([62]), 'Shift_L': set([50]), 'Control_L': set([37])}
2013-01-06 15:30:29,302 client has requested compression level=1
2013-01-06 15:30:29,303 process clipboard packet type=clipboard-token
2013-01-06 15:30:29,303 process clipboard token selection=CLIPBOARD, local clipboard name=CLIPBOARD
2013-01-06 15:30:29,303 got token, selection=CLIPBOARD
2013-01-06 15:30:29,303 process clipboard packet type=clipboard-token
2013-01-06 15:30:29,303 process clipboard token selection=PRIMARY, local clipboard name=PRIMARY
2013-01-06 15:30:29,303 got token, selection=PRIMARY
2013-01-06 15:30:29,304 process clipboard packet type=clipboard-token
2013-01-06 15:30:29,304 process clipboard token selection=SECONDARY, local clipboard name=SECONDARY
2013-01-06 15:30:29,304 got token, selection=SECONDARY
2013-01-06 15:30:29,304 server_settings: old={'resource-manager': 'Xft.dpi:\t96\n\n'}, updating with={'xsettings-blob': (0, ((0, 'Xft/Hinting', 4294967295L, 0), (1, 'Gtk/ToolbarStyle', 'icons', 0), (0, 'Net/CursorBlinkTime', 1200, 0), (1, 'Gtk/KeyThemeName', '', 0), (1, 'Net/SoundThemeName', 'default', 0), (1, 'Gtk/FontName', 'Sans 10', 0), (0, 'Gtk/ToolbarIconSize', 3, 0), (0, 'Net/EnableInputFeedbackSounds', 0, 0), (0, 'Gtk/ButtonImages', 1, 0), (0, 'Net/EnableEventSounds', 0, 0), (0, 'Net/CursorBlink', 1, 0), (0, 'Net/DoubleClickDistance', 5, 0), (1, 'Net/IconThemeName', 'Tango', 0), (0, 'Gtk/CanChangeAccels', 0, 0), (1, 'Gtk/ColorPalette', 'black:white:gray50:red:purple:blue:light blue:green:yellow:orange:lavender:brown:goldenrod4:dodger blue:pink:light green:gray10:gray30:gray75:gray90', 0), (1, 'Gtk/IMPreeditStyle', '', 0), (0, 'Xft/Antialias', 1, 0), (1, 'Gtk/IMModule', '', 0), (0, 'Net/DoubleClickTime', 250, 0), (1, 'Gtk/IMStatusStyle', '', 0), (0, 'Net/DndDragThreshold', 8, 0), (1, 'Gtk/CursorThemeName', '', 0), (1, 'Gtk/IconSizes', '', 0), (1, 'Net/ThemeName', 'Xfce', 0), (1, 'Xft/HintStyle', 'hintslight', 0), (1, 'Gtk/MenuBarAccel', 'F10', 0), (0, 'Gtk/MenuImages', 1, 0), (0, 'Gtk/CursorThemeSize', 0, 0), (1, 'Xft/RGBA', 'none', 0)))}
2013-01-06 15:30:29,305 sending message to 0x44L
2013-01-06 15:30:29,305 format_xsettings((0, ((0, 'Xft/Hinting', 4294967295L, 0), (1, 'Gtk/ToolbarStyle', 'icons', 0), (0, 'Net/CursorBlinkTime', 1200, 0), (1, 'Gtk/KeyThemeName', '', 0), (1, 'Net/SoundThemeName', 'default', 0), (1, 'Gtk/FontName', 'Sans 10', 0), (0, 'Gtk/ToolbarIconSize', 3, 0), (0, 'Net/EnableInputFeedbackSounds', 0, 0), (0, 'Gtk/ButtonImages', 1, 0), (0, 'Net/EnableEventSounds', 0, 0), (0, 'Net/CursorBlink', 1, 0), (0, 'Net/DoubleClickDistance', 5, 0), (1, 'Net/IconThemeName', 'Tango', 0), (0, 'Gtk/CanChangeAccels', 0, 0), (1, 'Gtk/ColorPalette', 'black:white:gray50:red:purple:blue:light blue:green:yellow:orange:lavender:brown:goldenrod4:dodger blue:pink:light green:gray10:gray30:gray75:gray90', 0), (1, 'Gtk/IMPreeditStyle', '', 0), (0, 'Xft/Antialias', 1, 0), (1, 'Gtk/IMModule', '', 0), (0, 'Net/DoubleClickTime', 250, 0), (1, 'Gtk/IMStatusStyle', '', 0), (0, 'Net/DndDragThreshold', 8, 0), (1, 'Gtk/CursorThemeName', '', 0), (1, 'Gtk/IconSizes', '', 0), (1, 'Net/ThemeName', 'Xfce', 0), (1, 'Xft/HintStyle', 'hintslight', 0), (1, 'Gtk/MenuBarAccel', 'F10', 0), (0, 'Gtk/MenuImages', 1, 0), (0, 'Gtk/CursorThemeSize', 0, 0), (1, 'Xft/RGBA', 'none', 0)))) serial=0, 29 settings
Traceback (most recent call last):
  File "/usr/lib/python2.7/dist-packages/xpra/server.py", line 1172, in _process_server_settings
    self.update_server_settings(packet[1])
  File "/usr/lib/python2.7/dist-packages/xpra/server.py", line 1209, in update_server_settings
    self._xsettings_manager = XSettingsManager(v)
  File "/usr/lib/python2.7/dist-packages/xpra/xposix/xsettings.py", line 34, in __init__
    self._set_blob_in_place(settings_blob)
  File "/usr/lib/python2.7/dist-packages/xpra/xposix/xsettings.py", line 40, in _set_blob_in_place
    settings_blob)
  File "/usr/lib/python2.7/dist-packages/wimpiggy/prop.py", line 293, in prop_set
    _prop_encode(target, etype, value))
  File "/usr/lib/python2.7/dist-packages/wimpiggy/prop.py", line 274, in _prop_encode
    return _prop_encode_scalar(disp, etype, value)
  File "/usr/lib/python2.7/dist-packages/wimpiggy/prop.py", line 279, in _prop_encode_scalar
    return (atom, formatbits, serialize(disp, value))
  File "/usr/lib/python2.7/dist-packages/wimpiggy/xsettings_prop.py", line 100, in set_settings
    assert type(value)==int, "invalid value type (int wanted): %s" % type(value)
AssertionError: invalid value type (int wanted): <type 'long'>

This is on Xfce desktop environment (sorry for not mentioning this earlier).

I had few more cases of accidental keyboard switching (with or without keyboard sync). I was only able to recover by attaching from remote computer and switching keyboard back to English. So here is the thought: as a remedy is it possible to reset keyboard layout on attaching?

I'll try to get "-d all" log demonstrating problem with accidental layout switching.


Sun, 06 Jan 2013 05:59:56 GMT - Antoine Martin: summary changed

OK, the XSETTINGS problem should be fixed in r2451, please confirm so I can backport to the v0.7.x branch.


As for layout switching, we may be able to do better than just resetting on connect (maybe we can do that too), by allowing the user to select the active layout via the system tray. I have seen Xkb code that does just that. I just hope that getting notifications when the layout is changed from underneath us is as easy so we can keep the tray in sync with the current server keyboard layout state.


Sun, 06 Jan 2013 09:02:43 GMT - onlyjob:

Replying to antoine:

OK, the XSETTINGS problem should be fixed in r2451, please confirm so I can backport to the v0.7.x branch.

I applied r2451 on top of r2443 and r2444 (0.7.6) and hereby confirm that there is no more errors when connecting to local session. Thank you. I'm looking forward to try your lovely idea to integrate keyboard selection to tray. :) Cheers.


Sun, 13 Jan 2013 23:06:43 GMT - onlyjob: attachment set

local connection, switched to nat layout just before disconnection.


Sun, 13 Jan 2013 23:09:26 GMT - onlyjob:

As promised I attached log (-d all) of the situation when in local connection keyboard switches to national layout without user pressing corresponding key combination. I disconnected the session as soon as I noticed the problem.


Mon, 11 Feb 2013 16:27:59 GMT - Antoine Martin: milestone changed


Wed, 20 Mar 2013 14:18:57 GMT - Antoine Martin: milestone changed

I think this badly needs Xkb, but I will try to expose the layouts thing via the applet in a future release, far too late for 0.9 though.. re-scheduling it.


Tue, 16 Jul 2013 06:30:30 GMT - Antoine Martin: status, milestone changed

Re-scheduling, Xkb work is due for 0.11


Thu, 17 Oct 2013 07:36:31 GMT - Antoine Martin: owner, status, milestone changed

re-scheduling (again)


Thu, 13 Feb 2014 14:29:25 GMT - Antoine Martin: owner, status changed

In order to allow the client to select a particular keyboard layout at runtime, we must first identify which layouts the client would like to select from... And I can't see anything in the data above that indicates more than one layout. Since you seem to be using a dual layout configuration, can you please post more data so we can try to see where the extra layouts can be found? The wiki/Keyboard may have some helpful pointers, see also Keyboard Configuration in Xorg


Wed, 05 Mar 2014 08:47:06 GMT - Antoine Martin: status changed; resolution set

Please follow up in #86 which has more information, if a bit dated, and a patch which should allow you to select the layout manually.


Sat, 23 Jan 2021 04:49:03 GMT - migration script:

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