xpra icon
Bug tracker and wiki

Opened 6 years ago

Closed 6 years ago

Last modified 6 years ago

#456 closed defect (fixed)

swap command and control key for mac osx clients

Reported by: alas Owned by: alas
Priority: minor Milestone: 0.11
Component: client Version:
Keywords: osx keyboard Cc:

Description (last modified by Antoine Martin)

When using osx one expects command-c to copy, for instance... but the osx client xpra sessions dont' recognize command-c - rather recognizing ctrl-c as a windows client would expect.

Ideally, the ctrl functionality can be mapped to command for the osx client.

(Oddly, it seems that the xpra interprets the command key as a non-sticky NumLock key.)

I'm attaching a (hopefully) illustrative file of the behavior with the gtk_view_keyboard tool.

Attachments (10)

xpra_keyboard_output_control-v-command (2.4 KB) - added by alas 6 years ago.
osx xpra command v. ctrl key output
osx-local-ctrl-v-cmd-key-mappings.txt (1.0 KB) - added by alas 6 years ago.
osx local key mappings of ctrl and command
osx-swapkeys.patch (2.9 KB) - added by Antoine Martin 6 years ago.
swap control and meta keys
osx-swapkeys+numlock.patch (3.1 KB) - added by Antoine Martin 6 years ago.
swap control and meta keys and also deal with numlock as per #453
osx-swapkeys+numlock-v2.patch (2.5 KB) - added by Antoine Martin 6 years ago.
swap control and meta keys and also deal with numlock as per #453 (also emulate numlock state)
xpra-cmd-test1 (139.5 KB) - added by alas 6 years ago.
xpra 0.11r4759 osx 10.9 keyboard_debug output
xpra-cmd-test2 (105.7 KB) - added by alas 6 years ago.
xpra 0.11r4759 osx 10.9 keyboard_debug output (from gedit)
xpra-cmd-local-test3.txt (1.1 KB) - added by alas 6 years ago.
another local Keyboard_Tool test, for very direct comparison (same keys as gedit)
apple-qwerty.jpg (61.1 KB) - added by Antoine Martin 6 years ago.
shows a typical mac keyboard layout
osx-ctrl-v-cmd-finaltest2.txt (37.7 KB) - added by alas 6 years ago.
osx ctrl v cmd test confirmation of key swappings

Download all attachments as: .zip

Change History (23)

Changed 6 years ago by alas

osx xpra command v. ctrl key output

comment:1 Changed 6 years ago by Antoine Martin

Description: modified (diff)
Keywords: keyboard added; command v ctrl removed
Milestone: 0.11
Owner: changed from Antoine Martin to Antoine Martin
Status: newassigned
Summary: osx client treats ctrl key as osx would generally treat the command keyswap command and control key for mac osx clients

Updating bug title: the client does exactly what is expected (bar NumLock issues) but we need to swap the two keys around to make *nix applications do what mac users expect in terms of key mapping.

comment:2 Changed 6 years ago by Antoine Martin

Please post *raw* keyboard data, without xpra interfering.

Looks like some modifier is mis-mapped and causing NumLock to fire.

Changed 6 years ago by alas

osx local key mappings of ctrl and command

comment:3 Changed 6 years ago by alas

Attached *raw* keyboard data as requested. osx 10.9, using Xpra.app/Contents/Helpers/Keyboard_Tool.

Included ctrl, ctrl-c, ctrl-v, command, cmd-c, cmd-v ... with both mac and microsoft keyboards.

Let me know if I overlooked anything.

Changed 6 years ago by Antoine Martin

Attachment: osx-swapkeys.patch added

swap control and meta keys

Changed 6 years ago by Antoine Martin

Attachment: osx-swapkeys+numlock.patch added

swap control and meta keys and also deal with numlock as per #453

comment:4 Changed 6 years ago by Antoine Martin

The patch osx-swapkeys+numlock.patch fixes both this bug and #453 for me.

Please confirm so I can apply and backport to v0.10.x

Changed 6 years ago by Antoine Martin

swap control and meta keys and also deal with numlock as per #453 (also emulate numlock state)

comment:5 Changed 6 years ago by Antoine Martin

Applied in r4747, this works for me and allows me to toggle numlock on and off.

Note:

  • numlock always starts "ON" since that is what most mac keyboards seem to do, and we do not detect the actual state (not sure how we could - looks very hard to do) - if the user has numlock off when starting the xpra session, then the numlock state will continue to be the opposite of what the user expects (the opposite of what may be shown by the little green light)
  • the swapping of "control" and "meta" may not be suitable for the v0.10.x branch, which is a bug-fix only branch after all

FYI: to apply the patches posted in tickets, you usually do:

cd xpra/trunk/src
patch -p0 < /path/to/osx-swapkeys+numlock-v2.patch​

comment:6 Changed 6 years ago by Antoine Martin

Added keyboard debug in r4749, use it with:

XPRA_KEYBOARD_DEBUG=1 xpra attach ...

comment:7 Changed 6 years ago by Antoine Martin

Owner: changed from Antoine Martin to alas
Status: assignednew

If meta/control swapping does not work for you (worked OK for me when I tested with xterm and gedit: control-C to stop terminal commands and usual cut&paste shortcuts) then please post the output of the client running with the debug option from comment:6

Note: with r4751 you can toggle the swapping of keys at runtime. (adding a command line switch was a bit harder so I didn't bother)

Last edited 6 years ago by Antoine Martin (previous) (diff)

Changed 6 years ago by alas

Attachment: xpra-cmd-test1 added

xpra 0.11r4759 osx 10.9 keyboard_debug output

Changed 6 years ago by alas

Attachment: xpra-cmd-test2 added

xpra 0.11r4759 osx 10.9 keyboard_debug output (from gedit)

Changed 6 years ago by alas

Attachment: xpra-cmd-local-test3.txt added

another local Keyboard_Tool test, for very direct comparison (same keys as gedit)

comment:8 Changed 6 years ago by alas

Attached files of output with keyboard_debug=1... xpra 0.11r4759, osx 10.9 ...

xpra-cmd-test1:
Started with xterms, started google-chrome, clicked youtube link, started video & first tried cmd-c then cmd-v to copy/paste some text... then tried the same with ctrl-c then ctrl-v. cmd-v output a 'v'. ctrl-v output nothing.

xpra-cmd-test2:
Started with xterms, started gedit, typed in text (I am a happy camper). Highlit & cmd-c ‘happy’… replaced text with ‘c’. Tried cmd-v, output ‘v’. Highlit & ctrl-c ‘camper’ … nothing happened. Tried ctrl-v… opened ‘View’ drop-menu.

xpra-cmd-local-test3:
Just for the sake of thoroughness. Same keyboard as test1 and 2, same keystrokes as gedit test (test2).

Changed 6 years ago by Antoine Martin

Attachment: apple-qwerty.jpg added

shows a typical mac keyboard layout

comment:9 Changed 6 years ago by Antoine Martin

Owner: changed from alas to Antoine Martin
Status: newassigned

It just occurred to me that the mac keyboards also swap the *location* of command and "option", see:
/raw-attachment/ticket/456/apple-qwerty.jpg
So maybe virtualbox does this translation for me already... Did you try hitting the "option" key instead during testing?

(we just ignore "mod2" which is numlock)

Anyway, those logs are fairly long, here's what I am really interested in:

  • when typing control-C only (from xpra-cmd-test2), I see:
    mask_to_names(<flags 0 of type GdkModifierType>)=['mod2']
    process_key_event swapping Control_L for Meta_L
    send_key_action(3, <GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'group': 0, 'string': '', 'keyname': 'Meta_L', 'pressed': True, 'keyval': 65507, 'keycode': 59}>)
    mask_to_names(<flags GDK_CONTROL_MASK of type GdkModifierType>) meta_modifier=mod1, control_modifier=control
    mask_to_names(<flags GDK_CONTROL_MASK of type GdkModifierType>)=['mod1', 'mod2']
    send_key_action(3, <GTKKeyEvent object, contents: {'modifiers': ['mod1', 'mod2'], 'group': 0, 'string': 'c', 'keyname': 'c', 'pressed': True, 'keyval': 99, 'keycode': 8}>)
    mask_to_names(<flags GDK_CONTROL_MASK of type GdkModifierType>) meta_modifier=mod1, control_modifier=control
    mask_to_names(<flags GDK_CONTROL_MASK of type GdkModifierType>)=['mod1', 'mod2']
    send_key_action(3, <GTKKeyEvent object, contents: {'modifiers': ['mod1', 'mod2'], 'group': 0, 'string': 'c', 'keyname': 'c', 'pressed': False, 'keyval': 99, 'keycode': 8}>)
    mask_to_names(<flags GDK_CONTROL_MASK of type GdkModifierType>) meta_modifier=mod1, control_modifier=control
    mask_to_names(<flags GDK_CONTROL_MASK of type GdkModifierType>)=['mod1', 'mod2']
    process_key_event swapping Control_L for Meta_L
    send_key_action(3, <GTKKeyEvent object, contents: {'modifiers': ['mod1', 'mod2'], 'group': 0, 'string': '', 'keyname': 'Meta_L', 'pressed': False, 'keyval': 65507, 'keycode': 59}>)
    
  • doing the same thing on my vbox machine:
    mask_to_names(<flags 0 of type GdkModifierType>)=[]
    process_key_event swapping Control_L for Meta_L
    send_key_action(1, <GTKKeyEvent object, contents: {'modifiers': [], 'group': 0, 'string': '', 'keyname': 'Meta_L', 'pressed': True, 'keyval': 65507, 'keycode': 59}>)
    mask_to_names(<flags GDK_CONTROL_MASK of type GdkModifierType>) meta_modifier=mod1, control_modifier=control
    mask_to_names(<flags GDK_CONTROL_MASK of type GdkModifierType>)=['mod1']
    send_key_action(1, <GTKKeyEvent object, contents: {'modifiers': ['mod1'], 'group': 0, 'string': 'c', 'keyname': 'c', 'pressed': True, 'keyval': 99, 'keycode': 8}>)
    mask_to_names(<flags GDK_CONTROL_MASK of type GdkModifierType>) meta_modifier=mod1, control_modifier=control
    mask_to_names(<flags GDK_CONTROL_MASK of type GdkModifierType>)=['mod1']
    send_key_action(1, <GTKKeyEvent object, contents: {'modifiers': ['mod1'], 'group': 0, 'string': 'c', 'keyname': 'c', 'pressed': False, 'keyval': 99, 'keycode': 8}>)
    mask_to_names(<flags GDK_CONTROL_MASK of type GdkModifierType>) meta_modifier=mod1, control_modifier=control
    mask_to_names(<flags GDK_CONTROL_MASK of type GdkModifierType>)=['mod1']
    process_key_event swapping Control_L for Meta_L
    send_key_action(1, <GTKKeyEvent object, contents: {'modifiers': ['mod1'], 'group': 0, 'string': '', 'keyname': 'Meta_L', 'pressed': False, 'keyval': 65507, 'keycode': 59}>)
    
    Is identical, bar the "numlock"="mod2" which is unset in my case. So swapping "Control" for "Meta" works exactly the same. We don't change the actual keycode just the keyname and the list of modifiers from then on, but that doesn't seem to be necessary (though I will try to handle that in a future patch).
  • when typing command-C only (from xpra-cmd-test2), I see:
    mask_to_names(<flags 0 of type GdkModifierType>)=['mod2']
    process_key_event swapping Meta_L for Control_L
    send_key_action(3, <GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'group': 0, 'string': '', 'keyname': 'Control_L', 'pressed': True, 'keyval': 65511, 'keycode': 55}>)
    mask_to_names(<flags GDK_MOD2_MASK | GDK_META_MASK of type GdkModifierType>) meta_modifier=mod1, control_modifier=control
    mask_to_names(<flags GDK_MOD2_MASK | GDK_META_MASK of type GdkModifierType>)=['mod2']
    send_key_action(3, <GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'group': 0, 'string': 'c', 'keyname': 'c', 'pressed': True, 'keyval': 99, 'keycode': 8}>)
    mask_to_names(<flags GDK_MOD2_MASK | GDK_META_MASK of type GdkModifierType>) meta_modifier=mod1, control_modifier=control
    mask_to_names(<flags GDK_MOD2_MASK | GDK_META_MASK of type GdkModifierType>)=['mod2']
    send_key_action(3, <GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'group': 0, 'string': 'c', 'keyname': 'c', 'pressed': False, 'keyval': 99, 'keycode': 8}>)
    mask_to_names(<flags GDK_MOD2_MASK | GDK_META_MASK of type GdkModifierType>) meta_modifier=mod1, control_modifier=control
    mask_to_names(<flags GDK_MOD2_MASK | GDK_META_MASK of type GdkModifierType>)=['mod2']
    process_key_event swapping Meta_L for Control_L
    send_key_action(3, <GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'group': 0, 'string': '', 'keyname': 'Control_L', 'pressed': False, 'keyval': 65511, 'keycode': 55}>)
    
  • doing the same thing on my vbox machine:
    mask_to_names(<flags 0 of type GdkModifierType>)=[]
    send_key_action(1, <GTKKeyEvent object, contents: {'modifiers': [], 'group': 1, 'string': '', 'keyname': 'Alt_L', 'pressed': True, 'keyval': 65513, 'keycode': 58}>)
    mask_to_names(<flags GDK_MOD1_MASK of type GdkModifierType>) meta_modifier=mod1, control_modifier=control
    mask_to_names(<flags GDK_MOD1_MASK of type GdkModifierType>)=['control']
    send_key_action(1, <GTKKeyEvent object, contents: {'modifiers': ['control'], 'group': 1, 'string': '\xc3\xa7', 'keyname': 'ccedilla', 'pressed': True, 'keyval': 231, 'keycode': 8}>)
    mask_to_names(<flags GDK_MOD1_MASK of type GdkModifierType>) meta_modifier=mod1, control_modifier=control
    mask_to_names(<flags GDK_MOD1_MASK of type GdkModifierType>)=['control']
    send_key_action(1, <GTKKeyEvent object, contents: {'modifiers': ['control'], 'group': 1, 'string': '\xc3\xa7', 'keyname': 'ccedilla', 'pressed': False, 'keyval': 231, 'keycode': 8}>)
    mask_to_names(<flags GDK_MOD1_MASK of type GdkModifierType>) meta_modifier=mod1, control_modifier=control
    mask_to_names(<flags GDK_MOD1_MASK of type GdkModifierType>)=['control']
    send_key_action(1, <GTKKeyEvent object, contents: {'modifiers': ['control'], 'group': 0, 'string': '', 'keyname': 'Alt_L', 'pressed': False, 'keyval': 65513, 'keycode': 58}>)
    mask_to_names(<flags 0 of type GdkModifierType>) meta_modifier=mod1, control_modifier=control
    
    And here we see that the key that I press comes up as "MOD1" whereas the real key for "command" seems to be "META"...

So, as I understand it:

  • VirtualBox swaps the "Left Windows-Start-Menu" key (aka "option" key in osx) with the "Alt_L" key (aka "command" key in osx)
  • VirtualBox intercepts the "Left Windows-Start-Menu" key so I cannot use it to trigger "META"

Fortunately, it doesn't do anything to the "Right Windows-Start-Menu" key and that one does come up as "META" in the VM.

Which means that I still need to:

  • change the code to handle "META"
  • handle both left and right keys, so maybe patch the mask before it gets translated into names
  • translate keycodes (and keyval?) too so this will be more correct and future proof
Last edited 6 years ago by Antoine Martin (previous) (diff)

comment:10 Changed 6 years ago by Antoine Martin

Owner: changed from Antoine Martin to alas
Status: assignednew

("META" is "command")

  • r4760 adds a FIXME not explaining why this ends up being harder to implement than it should be
  • r4761 properly detects the META key seen on OSX, and swaps it for "control"
  • r4762 ensures that we not only send the proper list of modifiers with key presses but that the actual keypresses (of either control or meta) end up with valid translated keysyms and keycodes (if at all possible)
  • r4763 adds some debug logging when swapping modifiers


Works for me, but as I said in comment:9 I cannot press the left command key through virtualbox... only the right one!

So, please re-test, you should see this in the log:

  • the key translation map (near the beginning):
    set_modifier_mappings(..) key translations={'Meta_R': (59, 'Control_L'), 'Control_L': (55, 'Alt_L'), 'Meta_L': (59, 'Control_L'), 'Control_R': (54, 'Alt_R')}
    
  • when pressing control:
    swap keys: translating key \
        '<GTKKeyEvent object, contents: {'modifiers': ['mod1', 'mod2'], 'group': 0, 'string': '', 'keyname': 'Control_L', 'pressed': False, 'keyval': 65507, 'keycode': 59}>' \
        to (55, 'Alt_L')
    
  • when pressing meta (Meta_R in my case):
    swap keys: translating key \
        '<GTKKeyEvent object, contents: {'modifiers': ['mod2'], 'group': 0, 'string': '', 'keyname': 'Meta_R', 'pressed': True, 'keyval': 65512, 'keycode': 54}>' \
        to (59, 'Control_L')
    
  • when pressing other keys, buttons or simply moving the mouse around with "control" held (*):
    mask_to_names swapping control for meta: control for mod1
    
  • when pressing other keys, buttons or simply moving the mouse around with "meta" (aka "command") held (*):
    mask_to_names swapping meta for control: mod1 for control
    
  • toggling the swap keys option in the menu should revert to "normal" behaviour (the one that confuses apple users)

(*) Note: some modifier keys will not show when moving the mouse around, that is the case for me with the "right windows start menu" key under virtualbox. I'm not sure if this is because of virtualbox, osx or gtk... Doesn't seem to cause any problems though.

Changed 6 years ago by alas

osx ctrl v cmd test confirmation of key swappings

comment:11 Changed 6 years ago by alas

Testing confirms that the swapping is behaving as you'd hoped. (I clicked both the right and left command keys, resulting in 'Meta_R', 'pressed': True in the one case and :False in the other.

Attached the output in case you'd like to look at more debugging output.

comment:12 Changed 6 years ago by Antoine Martin

Resolution: fixed
Status: newclosed

Good, closing at last.

This will not be backported to v0.10.x as this is too intrusive.

comment:13 Changed 6 years ago by Antoine Martin

Fix menu entry to say Command and not Option in r6434. Will backport.

Note: See TracTickets for help on using tickets.