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.
osx xpra command v. ctrl key output
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.
Please post *raw* keyboard data, without xpra interfering.
Looks like some modifier is mis-mapped and causing NumLock
to fire.
osx local key mappings of ctrl and command
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.
swap control and meta keys
swap control and meta keys and also deal with numlock as per #453
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
swap control and meta keys and also deal with numlock as per #453 (also emulate numlock state)
Applied in r4747, this works for me and allows me to toggle numlock on and off.
Note:
FYI: to apply the patches posted in tickets, you usually do:
cd xpra/trunk/src patch -p0 < /path/to/osx-swapkeys+numlock-v2.patch
Added keyboard debug in r4749, use it with:
XPRA_KEYBOARD_DEBUG=1 xpra attach ...
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)
xpra 0.11r4759 osx 10.9 keyboard_debug output
xpra 0.11r4759 osx 10.9 keyboard_debug output (from gedit)
another local Keyboard_Tool test, for very direct comparison (same keys as gedit)
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).
shows a typical mac keyboard layout
It just occurred to me that the mac keyboards also swap the *location* of command and "option", see:
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:
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}>)
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).
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}>)
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=controlAnd 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:
META
"
("META
" is "command
")
FIXME
not explaining why this ends up being harder to implement than it should be
META
key seen on OSX, and swaps it for "control
"
control
or meta
) end up with valid translated keysyms and keycodes (if at all possible)
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:
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')}
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')
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')
mask_to_names swapping control for meta: control for mod1
mask_to_names swapping meta for control: mod1 for control
(*) 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.
osx ctrl v cmd test confirmation of key swappings
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.
Good, closing at last.
This will not be backported to v0.10.x as this is too intrusive.
Fix menu entry to say Command
and not Option
in r6434. Will backport.
this ticket has been moved to: https://github.com/Xpra-org/xpra/issues/456