4

One of my key bindings is not working on one particular machine and I don't understand why.

On Ubuntu 14.04, with the emacs24 package version 24.3+1-2ubuntu1, I start emacs -Q (under X) and run

(global-set-key [(meta backspace)] 'undo)

then press Ctrl+H, c, Alt+BackSpace.

On machine 1, I see as expected that

<M-backspace> runs the command undo

On machine 2, I see

M-DEL (translated from <M-backspace>) runs the command backward-kill-word

The machines are running the same distribution and should be configured pretty much identically. The Emacs packages are the same, yet they react differently even under emacs -Q.

After a bit of investigation, it turns out that the culprit is input-decode-map, which on machine 2 contains

(keymap
 (27 keymap
     (C-backspace .
          [27 C-delete])
     (C-delete .
           [27 C-backspace]))
 (C-M-backspace .
        [C-M-delete])
 (C-M-delete .
         [C-M-backspace])
 (M-backspace .
          [M-delete])
 (M-delete .
       [M-backspace]))

On machine 1, the keymap contains the same keys, but they're all bound to nil.

  1. Why does input-decode-map exchange BackSpace and Delete on machine 2?
  2. How do I fix this? I can obviously remove the entries, but there may be terminals where it's warranted; how do I change input-decode-map on X11 only (keeping in mind that I do sometimes open terminal and X11 frames in the same instance, so testing window-system in .emacs isn't good enough)?

2 Answers2

4

It turns out that Delete (delete) is also translated to DEL rather than deletechar; this happens in local-function-key-map, not in input-decode-map, so I didn't notice it at first because my keybindings overrode that.

The manual has a section “If <DEL> Fails to Delete”, which pointed me to normal-erase-is-backspace, which is used when a frame is created in the function normal-erase-is-backspace-setup-frame from simple.el and normal-erase-is-backspace has the value maybe (that last is the case on both machines). For X11, this calls the native function x-backspace-delete-keys-p.

Check if both Backspace and Delete keys are on the keyboard of FRAME. FRAME nil means use the selected frame.
Value is t if we know that both keys are present, and are mapped to the usual X keysyms. Value is lambda if we cannot determine if both keys are present and mapped to the usual X keysyms.

Looking at the C implementation, it uses XKB and tests whether the physical key DELE sends the keysym Delete and the physical key BKSP sends the keysym BackSpace. This is normally the case, but machine 2 is a laptop with cursor keys placed awkwardly, and I used xmodmap to shuffle a few keysyms around including Delete. Thus on machine 2 DELE doesn't send Delete, so x-backspace-delete-keys-p returns nil, so normal-erase-is-backspace-setup-frame calls normal-erase-is-backspace-mode with the argument 0, which causes this undesired remapping (delete to DEL in local-function-key-map, M-backspace to M-delete in input-decode-map, etc.).

One fix would be to use XKB rather than xmodmap to shuffle my cursor keys, and declare the physical keys DELE and others as having different keycodes, rather than declare the keycodes to have different keysyms. See my follow-up question on Unix & Linux.

Inside Emacs, my blunt fix for now is to force x-backspace-delete-keys-p to return t.

(when (subrp (symbol-function 'x-backspace-delete-keys-p))
  (fset 'builtin-x-backspace-delete-keys-p
        (symbol-function 'x-backspace-delete-keys-p))
  (defun my-x-backspace-delete-keys-p ()
    t)
  (defalias 'x-backspace-delete-keys-p 'my-x-backspace-delete-keys-p)
  (if (eq window-system 'x)
      (mapc (lambda (frame)
              (when (frame-parameter frame 'display)
                (set-terminal-parameter frame 'normal-erase-is-backspace nil)
                (normal-erase-is-backspace-setup-frame frame)))
            (frame-list))))
-1

When you see DEL in Emacs-land, it doesn't mean the Delete key, it means the backspace control character (so it's [almost] synonymous with backspace).

From the doc string of (edmacro-mode) (which defines the keys that kbd uses):

  • The special words RET, SPC, TAB, DEL, LFD, ESC, and NUL represent special control characters. The words must be written in uppercase.

  • A word in angle brackets, e.g., , , or , represents a function key. (Note that in the standard configuration, the function key and the control key RET are synonymous.) You can use angle brackets on the words RET, SPC, etc., but they are not required there.

So Emacs is not swapping anything, you're just confused because it's key naming scheme is a bit odd.

backward-kill-word is the default binding for M-DEL. So, while I'm not exactly sure why your specific global-set-key isn't working on one machine (could be a weird keyboard or keyboard layout), I would bet that the following does:

(global-set-key (kbd "M-DEL") 'undo)

If not, try using (kbd "M-<backspace>"). In general, using kbd is almost always better than trying to figure out how to write the keys manually with the proper escapes, codes, etc. It's more readable and consistent.

nanny
  • 5,704
  • 18
  • 38
  • Sorry, but I'm way ahead of you. I know what `DEL` means (it's not what you say, by the way: it means character 127, which may or may not be backspace depending on the terminal configuration). Emacs *is* swapping some keychords involving `backspace` with corresponding keychords involving `delete`, via `input-decode-map`. My `global-set-key` isn't working because `input-decode-map` acts at a lower level. I don't want to set `M-DEL` to `undo`, I want to set `M-backspace` to `undo` (it would make a difference on a terminal frame). – Gilles 'SO- stop being evil' Mar 04 '15 at 16:16
  • *(cont.)* `kbd` is more convenient than writing keys manually, but there's no reason to use it, and indeed in subtle cases it's an extra level of indirection that's best avoided. When I write `M-backspace`, I mean what I write. – Gilles 'SO- stop being evil' Mar 04 '15 at 16:17