xpra icon
Bug tracker and wiki

Opened 5 weeks ago

Last modified 4 weeks ago

#2804 assigned defect

OSX Alt/Option and Command behavior

Reported by: tscogland Owned by: Antoine Martin
Priority: major Milestone: 4.1
Component: client Version: trunk
Keywords: mac modifiers keyboard Cc:

Description

I'm using the most recent binary version 4.1 r26306 on MacOS 10.14.6 (18G4032), the server is xpra v4.0-r26350 on ubuntu 19.10. Connecting with /Applications/Xpra.app/Contents/MacOS/Xpra attach ssh:localhost:1 --ssh="ssh -p 2202" --desktop-scaling=0 -d keyboard

Overall I love the updates and how this behaves on OSX now (serious props to anyone and everyone involved in that, it's really quite nice). The issue I'm having is dealing with modifier keys. As far as I can tell Shift and Control are working just fine, but alt/option and Command are behaving quite oddly.

Alt:
Pressing alt/option on the client sends Alt_L down as intended, releasing it generates a release, all looks good at first. The problem comes when I try to combine it with something else. This is xev output for pressing <Alt+a>:

KeyPress event, serial 33, synthetic NO, window 0xa00001,
    root 0x299, subw 0x0, time 508547501, (2247,-983), root:(2247,681),
    state 0x10, keycode 64 (keysym 0xffe9, Alt_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyRelease event, serial 36, synthetic NO, window 0xa00001,
    root 0x299, subw 0x0, time 508548682, (2247,-983), root:(2247,681),
    state 0x18, keycode 64 (keysym 0xffe9, Alt_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

Client keyboard debug prints from the same <Alt+a>:

2020-06-08 11:05:53,090 parse_key_event(<Gdk.EventKey object at 0x13275f220 (void at 0x7f847186f3d0)>, True)=<GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'keyname': 'Alt_L', 'keyval': 65513, 'keycode': 58, 'group': 1, 'string': '', 'pressed': True}>
2020-06-08 11:05:53,090 handle_key_action(ClientWindow(1), <GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'keyname': 'Alt_L', 'keyval': 65513, 'keycode': 58, 'group': 1, 'string': '', 'pressed': True}>) wid=1
2020-06-08 11:05:53,090 key_handled_as_shortcut(ClientWindow(1), 'Alt_L', ['mod2'], True) shortcuts=None
2020-06-08 11:05:53,090 send_key_action(1, <GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'keyname': 'Alt_L', 'keyval': 65513, 'keycode': 58, 'group': 1, 'string': '', 'pressed': True}>)
2020-06-08 11:05:54,207 mask_to_names(<flags GDK_MOD1_MASK of type Gdk.ModifierType>)=['mod2'] swap_keys=False, modmap={<flags GDK_SHIFT_MASK of type Gdk.ModifierType>: 'shift', <flags GDK_LOCK_MASK of type Gdk.ModifierType>: 'lock', <flags GDK_SUPER_MASK of type Gdk.ModifierType>: 'mod3', <flags GDK_HYPER_MASK of type Gdk.ModifierType>: 'mod4', <flags GDK_META_MASK of type Gdk.ModifierType>: 'mod1', <flags GDK_CONTROL_MASK of type Gdk.ModifierType>: 'control'}, num_lock_state=True, num_lock_modifier=mod2
2020-06-08 11:05:54,208 parse_key_event(<Gdk.EventKey object at 0x13275f270 (void at 0x7f847186f1f0)>, True)=<GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'keyname': 'aring', 'keyval': 229, 'keycode': 0, 'group': 1, 'string': '', 'pressed': True}>
2020-06-08 11:05:54,208 handle_key_action(ClientWindow(1), <GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'keyname': 'aring', 'keyval': 229, 'keycode': 0, 'group': 1, 'string': '', 'pressed': True}>) wid=1
2020-06-08 11:05:54,208 key_handled_as_shortcut(ClientWindow(1), 'aring', ['mod2'], True) shortcuts=None
2020-06-08 11:05:54,208 send_key_action(1, <GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'keyname': 'aring', 'keyval': 229, 'keycode': 0, 'group': 1, 'string': '', 'pressed': True}>)
2020-06-08 11:05:54,275 mask_to_names(<flags GDK_MOD1_MASK of type Gdk.ModifierType>)=['mod2'] swap_keys=False, modmap={<flags GDK_SHIFT_MASK of type Gdk.ModifierType>: 'shift', <flags GDK_LOCK_MASK of type Gdk.ModifierType>: 'lock', <flags GDK_SUPER_MASK of type Gdk.ModifierType>: 'mod3', <flags GDK_HYPER_MASK of type Gdk.ModifierType>: 'mod4', <flags GDK_META_MASK of type Gdk.ModifierType>: 'mod1', <flags GDK_CONTROL_MASK of type Gdk.ModifierType>: 'control'}, num_lock_state=True, num_lock_modifier=mod2
2020-06-08 11:05:54,275 parse_key_event(<Gdk.EventKey object at 0x13275f270 (void at 0x7f847186f3d0)>, False)=<GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'keyname': 'aring', 'keyval': 229, 'keycode': 0, 'group': 1, 'string': '', 'pressed': False}>
2020-06-08 11:05:54,275 handle_key_action(ClientWindow(1), <GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'keyname': 'aring', 'keyval': 229, 'keycode': 0, 'group': 1, 'string': '', 'pressed': False}>) wid=1
2020-06-08 11:05:54,275 key_handled_as_shortcut(ClientWindow(1), 'aring', ['mod2'], False) shortcuts=None
2020-06-08 11:05:54,275 send_key_action(1, <GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'keyname': 'aring', 'keyval': 229, 'keycode': 0, 'group': 1, 'string': '', 'pressed': False}>)
2020-06-08 11:05:54,343 mask_to_names(<flags GDK_MOD1_MASK of type Gdk.ModifierType>)=['mod2'] swap_keys=False, modmap={<flags GDK_SHIFT_MASK of type Gdk.ModifierType>: 'shift', <flags GDK_LOCK_MASK of type Gdk.ModifierType>: 'lock', <flags GDK_SUPER_MASK of type Gdk.ModifierType>: 'mod3', <flags GDK_HYPER_MASK of type Gdk.ModifierType>: 'mod4', <flags GDK_META_MASK of type Gdk.ModifierType>: 'mod1', <flags GDK_CONTROL_MASK of type Gdk.ModifierType>: 'control'}, num_lock_state=True, num_lock_modifier=mod2
2020-06-08 11:05:54,343 parse_key_event(<Gdk.EventKey object at 0x13275f360 (void at 0x7f84529aa1e0)>, False)=<GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'keyname': 'Alt_L', 'keyval': 65513, 'keycode': 58, 'group': 0, 'string': '', 'pressed': False}>
2020-06-08 11:05:54,343 handle_key_action(ClientWindow(1), <GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'keyname': 'Alt_L', 'keyval': 65513, 'keycode': 58, 'group': 0, 'string': '', 'pressed': False}>) wid=1
2020-06-08 11:05:54,343 key_handled_as_shortcut(ClientWindow(1), 'Alt_L', ['mod2'], False) shortcuts=None
2020-06-08 11:05:54,343 send_key_action(1, <GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'keyname': 'Alt_L', 'keyval': 65513, 'keycode': 58, 'group': 0, 'string': '', 'pressed': False}>)

The same appears to happen for any key combined with Alt.

As to Command, it sends an odd mishmash of modifiers for some reason, just pressing and releasing command gives me Shift and Meta left down, and Shift and *Alt* left up. Leaving meta left pressed, sort-of, and releasing a never pressed alt key:

xev:

KeyPress event, serial 33, synthetic NO, window 0xa00001,
    root 0x299, subw 0x0, time 508786645, (2881,-372), root:(2881,1292),
    state 0x10, keycode 50 (keysym 0xffe1, Shift_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyPress event, serial 33, synthetic NO, window 0xa00001,
    root 0x299, subw 0x0, time 508786645, (2881,-372), root:(2881,1292),
    state 0x11, keycode 64 (keysym 0xffe7, Meta_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyRelease event, serial 36, synthetic NO, window 0xa00001,
    root 0x299, subw 0x0, time 508786747, (2881,-372), root:(2881,1292),
    state 0x19, keycode 50 (keysym 0xffe1, Shift_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyRelease event, serial 36, synthetic NO, window 0xa00001,
    root 0x299, subw 0x0, time 508786747, (2881,-372), root:(2881,1292),
    state 0x18, keycode 64 (keysym 0xffe9, Alt_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

debug:

2020-06-08 11:07:43,862 parse_key_event(<Gdk.EventKey object at 0x132763f90 (void at 0x7f84418031f0)>, True)=<GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'keyname': 'Meta_L', 'keyval': 65511, 'keycode': 55, 'group': 0, 'string': '', 'pressed': True}>
2020-06-08 11:07:43,862 handle_key_action(GLClientWindow(11 : GLDrawingArea(11, (178, 178), None)), <GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'keyname': 'Meta_L', 'keyval': 65511, 'keycode': 55, 'group': 0, 'string': '', 'pressed': True}>) wid=11
2020-06-08 11:07:43,863 key_handled_as_shortcut(GLClientWindow(11 : GLDrawingArea(11, (178, 178), None)), 'Meta_L', ['mod2'], True) shortcuts=None
2020-06-08 11:07:43,863 send_key_action(11, <GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'keyname': 'Meta_L', 'keyval': 65511, 'keycode': 55, 'group': 0, 'string': '', 'pressed': True}>)
2020-06-08 11:07:43,962 mask_to_names(<flags GDK_MOD2_MASK | GDK_META_MASK of type Gdk.ModifierType>)=['mod1', 'mod2'] swap_keys=False, modmap={<flags GDK_SHIFT_MASK of type Gdk.ModifierType>: 'shift', <flags GDK_LOCK_MASK of type Gdk.ModifierType>: 'lock', <flags GDK_SUPER_MASK of type Gdk.ModifierType>: 'mod3', <flags GDK_HYPER_MASK of type Gdk.ModifierType>: 'mod4', <flags GDK_META_MASK of type Gdk.ModifierType>: 'mod1', <flags GDK_CONTROL_MASK of type Gdk.ModifierType>: 'control'}, num_lock_state=True, num_lock_modifier=mod2
2020-06-08 11:07:43,962 parse_key_event(<Gdk.EventKey object at 0x132765450 (void at 0x7f84529aa320)>, False)=<GTKKeyEvent object, contents: {'modifiers': ['mod1', 'mod2'], 'keyname': 'Meta_L', 'keyval': 65511, 'keycode': 55, 'group': 0, 'string': '', 'pressed': False}>
2020-06-08 11:07:43,963 handle_key_action(GLClientWindow(11 : GLDrawingArea(11, (178, 178), None)), <GTKKeyEvent object, contents: {'modifiers': ['mod1', 'mod2'], 'keyname': 'Meta_L', 'keyval': 65511, 'keycode': 55, 'group': 0, 'string': '', 'pressed': False}>) wid=11
2020-06-08 11:07:43,963 key_handled_as_shortcut(GLClientWindow(11 : GLDrawingArea(11, (178, 178), None)), 'Meta_L', ['mod1', 'mod2'], False) shortcuts=None
2020-06-08 11:07:43,963 send_key_action(11, <GTKKeyEvent object, contents: {'modifiers': ['mod1', 'mod2'], 'keyname': 'Meta_L', 'keyval': 65511, 'keycode': 55, 'group': 0, 'string': '', 'pressed': False}>)

If I combine command with another key, pressing the other key releases shift, but keeps meta, so I can kinda-sorta use some meta keys sometimes, but sometimes they act as alt keys. The overall effect is highly peculiar. Using they keyboard tool included in the toolbox shows me that the gtk keyboard signals being received on the client are all as I would expect. Is there a good way I can dig into this and possibly do some manual mapping to resolve the issue?

Change History (3)

comment:1 Changed 5 weeks ago by Antoine Martin

Status: newassigned

the server is xpra v4.0-r26350

Please try updating to the latest stable release. (which is 4.0.2 at time of writing)

Your log shows swap_keys=False but your command line does not specify swap-keys, why is that?
Does it work better with swap-keys enabled?

I don't have easy access to a macos system to test, so this may take a while to fix.
My virtualbox test systems mangle modifier keys because of the virtualization.

comment:2 Changed 4 weeks ago by tscogland

Upgraded the server to latest stable, behavior is identical.

I don't actually know, I assumed the default was off since that's what I would have set it to anyway, swapping keys like that would not go well for me as I switch between mac and linux regularly and just go by what application I'm using. If I do turn swap-keys on, pressing Control_L generates both Control_L and Alt_L, not releasing Alt_L until after a delay (though it does release it). Combining Control_L with another character generates something somewhat interesting, here's the xev output:

KeyPress event, serial 36, synthetic NO, window 0xc00001,
    root 0x299, subw 0x0, time 590341828, (90,-14), root:(90,1650),
    state 0x10, keycode 105 (keysym 0xffe4, Control_R), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyRelease event, serial 36, synthetic NO, window 0xc00001,
    root 0x299, subw 0x0, time 590344834, (90,-14), root:(90,1650),
    state 0x14, keycode 105 (keysym 0xffe4, Control_R), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyPress event, serial 36, synthetic NO, window 0xc00001,
    root 0x299, subw 0x0, time 590344834, (90,-14), root:(90,1650),
    state 0x10, keycode 64 (keysym 0xffe9, Alt_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyPress event, serial 36, synthetic NO, window 0xc00001,
    root 0x299, subw 0x0, time 590344834, (90,-14), root:(90,1650),
    state 0x18, keycode 38 (keysym 0x61, a), same_screen YES,
    XLookupString gives 1 bytes: (61) "a"
    XmbLookupString gives 1 bytes: (61) "a"
    XFilterEvent returns: False

KeyRelease event, serial 36, synthetic NO, window 0xc00001,
    root 0x299, subw 0x0, time 590344834, (90,-14), root:(90,1650),
    state 0x18, keycode 38 (keysym 0x61, a), same_screen YES,
    XLookupString gives 1 bytes: (61) "a"
    XFilterEvent returns: False

KeyRelease event, serial 36, synthetic NO, window 0xc00001,
    root 0x299, subw 0x0, time 590355484, (577,-630), root:(577,1034),
    state 0x18, keycode 64 (keysym 0xffe9, Alt_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

It generates a Control_R down, then nothing until I hit a, then Control_R up, Alt_L down, a, then Alt_L up on release. The end result is that control does emit a working alt modifier in swap keys mode, but with extra events for some reason. Here's Command+a in swap_keys as well:

KeyPress event, serial 36, synthetic NO, window 0xc00001,
    root 0x299, subw 0x0, time 590476514, (127,-16), root:(127,1648),
    state 0x10, keycode 50 (keysym 0xffe1, Shift_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyPress event, serial 36, synthetic NO, window 0xc00001,
    root 0x299, subw 0x0, time 590476514, (127,-16), root:(127,1648),
    state 0x11, keycode 64 (keysym 0xffe7, Meta_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyRelease event, serial 36, synthetic NO, window 0xc00001,
    root 0x299, subw 0x0, time 590479781, (127,-16), root:(127,1648),
    state 0x19, keycode 64 (keysym 0xffe7, Meta_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyRelease event, serial 36, synthetic NO, window 0xc00001,
    root 0x299, subw 0x0, time 590479781, (127,-16), root:(127,1648),
    state 0x11, keycode 50 (keysym 0xffe1, Shift_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyPress event, serial 36, synthetic NO, window 0xc00001,
    root 0x299, subw 0x0, time 590479781, (127,-16), root:(127,1648),
    state 0x10, keycode 37 (keysym 0xffe3, Control_L), same_screen YES,
    XLookupString gives 0 bytes: 
    XmbLookupString gives 0 bytes: 
    XFilterEvent returns: False

KeyPress event, serial 36, synthetic NO, window 0xc00001,
    root 0x299, subw 0x0, time 590479781, (127,-16), root:(127,1648),
    state 0x14, keycode 38 (keysym 0x61, a), same_screen YES,
    XLookupString gives 1 bytes: (01) ""
    XmbLookupString gives 1 bytes: (01) ""
    XFilterEvent returns: False

KeyRelease event, serial 36, synthetic NO, window 0xc00001,
    root 0x299, subw 0x0, time 590479781, (127,-16), root:(127,1648),
    state 0x14, keycode 38 (keysym 0x61, a), same_screen YES,
    XLookupString gives 1 bytes: (01) ""
    XFilterEvent returns: False

Again there are a number of spurious events, but the end result is a working control character code.

Ideally I'd prefer to have my modifiers map the way they would if this keyboard were plugged into the remote machine, command generate super, alt generate alt, and control generate control, but even getting to command generates alt would be a step forward. Is there any way to tweak the mappings other than digging into the tables used by swap_keys? I'm not much of a UI programmer, but I'm not bad with python and could try and help with this if you could help point me in the right direction.

comment:3 Changed 4 weeks ago by Antoine Martin

Is there any way to tweak the mappings other than digging into the tables used by swap_keys? I'm not much of a UI programmer, but I'm not bad with python and could try and help with this if you could help point me in the right direction.

The keyboard mapping code is the worst part of the whole project, it's messy and incomprehensible, even for me, and I wrote most of it.. I'm not trying to discourage you, this is just a warning and apology combined.

The mapping code is spread out all over the place, client side we have:

But most of the key mapping logic actually happens server side, and that's hard to comprehend!

Note: See TracTickets for help on using tickets.