Who stole “C-.”, “C-;” and possibly other keys from my Emacs?


Motivation: Why doesn’t Emacs respond to C-., C-; and some other keys?

When you are running Emacs occasionally you will notice that Emacs does not respond to some keys. This is because the Desktop enviornment-—if you are a GNOME user, the GNOME Desktop—steals those keys before it even reaches Emacs.

For example, ever since the embark package started recommending C-. key binding for embark-act, there have been reports of Emacs users binding the C-. key, only to find that it doesn’t work on GNOME Desktop.

If you are one such user, who has his C-., C-; and possibly other keys snatched away from your Emacs, read on …

Where does one find keyboard shortcuts used by the GNOME Desktop?

GNOME Control Center
You can find the keyboard shortcuts used by the GNOME Desktop using gnome-control-center. The keybindings used by the Desktop are listed under Keyboard Shortcuts.

gnome-control-center

Dconf Editor
You can find keyboard shortcuts used by various applications using the dconf-editor; just search for key in the Search box.

dconf-editor

To an experienced Emacs user, both of these options are as good as looking for a needle in the haystack. What these GUI tools report is piecemeal and you are left with a vague sense of unease.

gsettings
You can find the keyboard shortcuts using the gsettings shell command. For example, if the stolen key has a Control in there, you might try the following.

~$ gsettings list-recursively | grep -i 'Control'

org.freedesktop.ibus.general.hotkey trigger ['Control+space', 'Zenkaku_Hankaku', 'Alt+Kanji', 'Alt+grave', 'Hangul', 'Alt+Release+Alt_R']
org.freedesktop.ibus.panel.emoji hotkey ['<Control>period', '<Control>semicolon']
org.freedesktop.ibus.panel.emoji unicode-hotkey ['<Control><Shift>u']
org.gnome.Terminal.Legacy.Keybindings close-tab '<Control><Shift>w'
org.gnome.Terminal.Legacy.Keybindings close-window '<Control><Shift>q'
org.gnome.Terminal.Legacy.Keybindings copy '<Control><Shift>c'
...

Is there a better way to hunt for the stolen keys which suits the temperament and outlook of an Emacs user?

As you all know, an Emacs user is in a class of his own.

He doesn’t work with the best tools for a job at hand; instead he starts with a strongly-held belief-—often a mistaken belief-—that Emacs is the best tool for the job, whatever the job may be; After exploring existing solutions that could be used within Emacs, if he discovers that there is no such tool, he goes about creating one such tool.

Given what we know about a typical Emacs user, there indeed is a solution to for “stolen keys” problem; and it involves use of the gsettings.el — GSettings (Gnome) helpers.1

  1. Copy the following Emacs Lisp snippet to your *scratch* buffer, and do M-x eval-buffer.
    (require 'dash)
    (require 'rx)
    (require 'gsettings)
    (with-current-buffer
        (generate-new-buffer "*Gsettings*")
      (pop-to-buffer (current-buffer))
      (org-mode)
      (let ((case-fold-search t))
        (save-excursion
          (->>
           "list-recursively"
           gsettings--run
           gsettings--split-lines
           (--map
        (cond
         ((string-match (rx-to-string
                 '(and (group (one-or-more (not " ")))
                       (one-or-more " ")
                       (group (one-or-more (not " ")))
                       (one-or-more " ")
                       (group (one-or-more any))))
                it)
          (list (match-string 1 it)
            (match-string 2 it)
            (match-string 3 it)))
         (t (user-error "This shouldn't happen"))))
           (--select (and
              (let ((parsed (gvariant-parse (nth 2 it))))
                (not (and (consp parsed)
                      (eq (car parsed) 'parsec-error))))
              (or (string-match-p (rx-to-string
                           '(and "<"
                             (one-or-more any)
                             ">"))
                          (nth 2 it))
                  (string-match-p (rx-to-string
                           '(and "Keybindings"))
                          (nth 0 it)))))
           (--map (let* ((bindings (gvariant-parse (nth 2 it))))
            (cond
             ((vectorp bindings)
              (let ((prefix it))
                (--map (append (list it) (butlast prefix)) bindings)))
             (t
              (list (append (list bindings) (butlast it)))))))
           (-flatten-n 1)
           (--map (cons (format "%s" (car it)) (cdr it)))
           (--tree-map (format "%s" it))
           (--map (format "| %s |\n" (mapconcat #'identity it " | ")))
           (apply #'insert))))
      (org-table-align)
      (sort-lines nil (point-min) (point-max))
      (save-excursion
        (insert (format "|%s|%s|%s|\n" "Key Binding" "Schema" "Key"))
        (insert (format "|-\n")))
      (org-table-align))
    
  2. You will be presented with the following Org buffer, …
    | Key Binding | Schema                                       | Key                   |
    |-------------+----------------------------------------------+-----------------------|
    |             | org.gnome.settings-daemon.plugins.media-keys | help                  |
    | 30          | org.gnome.gnome-flashback.keybindings        | max-screencast-length |
    | <Alt>0      | org.gnome.Terminal.Legacy.Keybindings        | switch-to-tab-10      |
    | <Alt>1      | org.gnome.Terminal.Legacy.Keybindings        | switch-to-tab-1       |
    | <Alt>2      | org.gnome.Terminal.Legacy.Keybindings        | switch-to-tab-2       |
    | <Alt>3      | org.gnome.Terminal.Legacy.Keybindings        | switch-to-tab-3       |
    | <Alt>4      | org.gnome.Terminal.Legacy.Keybindings        | switch-to-tab-4       |
    | <Alt>5      | org.gnome.Terminal.Legacy.Keybindings        | switch-to-tab-5       |
    | <Alt>6      | org.gnome.Terminal.Legacy.Keybindings        | switch-to-tab-6       |
    | ...         | ...                                          | ...                   |
    

    A simple M-x occur RET Control RET in the resulting buffer, followed by some eye-balling will help you identify the GNOME component.

    For example, if you are looking for the C-., then the entry of interest is

    | <Control>period | org.freedesktop.ibus.panel.emoji | hotkey |
    
  3. Once you have identified the thief-—that is, the specific Gsettings key-—that needs to be re-configured, you can either use the GNOME Control Center, the component-specific GUI or the dconf-editor to re-configure or reset the keys. In the C-. and C-; case ,  it is the ibus panel, which is the thief, and you could reset the keys as reclaim the stolen keys as shown below.
    $ ibus-setup
    

  4. If you disdain using GUI-—you should be, because you are a pre-historic beast (beest?)-—you can do
    $ gsettings set org.freedesktop.ibus.panel.emoji hotkey  "@as []"
    

Bonus TIP: How to save and migrate your GSettings to some other machine

If you are having multiple work machines, then you can use

$ dconf dump / > dconf-settings.in
$ dconf load / < dconf-settings.in
$ dconf reset -f /

Conclusion

To an Emacs user, keyboard keys are a scarce resource; and having someone steal the already scarce set of keys is well-nigh a nightmare. So, it is good to know what to do in case Emacs doesn’t respond to some keys. The solution outlined here targets GNOME Desktop users. I will be happy to hear sob stories, and redemption options available for KDE, Windows, or Mac desktop users.

If you haven’t already noticed, the Emacs Lisp snippet for probing the GSettings for keybindings uses some heuristics for locating the keyboard shortcuts. Some heuristics in necessary; this is because the values in a GSettings database does not have a unique type for values that are keyboard shortcuts; the keyboard shortcuts are just a string or an array of strings. I have a hunch that the heuristic used by Emacs Lisp snippet could fall short, and fail to report an existing keyboard shortcut. So, be wary before you put full faith in the Emacs Lisp snippet above to do what it claims to do.

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this:
search previous next tag category expand menu location phone mail time cart zoom edit close