Where are GNOME keyboard shortcuts stored?

Ubuntu has changed since other answers to this question were written.

Keybindings have moved from gconf to dconf. Gconf stores its data in xml files and was accessed by gconf-editor and gconf-tool2. Dconf stores its data in a binary format and is accessed by dconf-editor and gsettings.

The number of places that keybindings are stored is reduced. There is now a centralized dconf path to store window manager keybindings (org.gnome.desktop.wm.keybindings). There are mapping files in the directory /usr/share/gnome-control-center/keybindings/that show how these are applied based on the window manager you are actually using (compiz or metacity).

A second set of non-window-manager related key bindings is stored in the dconf path org.gnome.settings-daemon.plugins.media-keys

There is a third set of keybindings related to power buttons that is stored in the dconf path org.gnome.settings-daemon.plugins.power. There is currently a bug in the GUI that lets you configure keybindings. It doesn't know about these settings. I have a "sleep" button on my keyboard. If I want to reassign it to other functionality, I have to disable the setting in org.gnome.settings-daemon.plugins.power manually. The GUI doesn't do it for me (although it assigns the new functionality just fine).

The other wrinkle is custom key bindings. These are stored in dconf using relocatable schema. Its done this way because there are an arbitrary number of them. A reasonable approach, but it makes listing or modifying them via the command line harder than it should be.

I also found out that the GUI that allows you to assign keybindings is limited in a way that annoys me. The GUI allows exactly ONE keybinding to be assigned to each action. In dconf, you can set an array of bindings for a single action. This is useful to me. For example, I like to have the "close-window" action assigned to the traditonal Alt-F4 as well as to an easier to hit single button on my keybord.

I have written a Perl script to dump all the keybindings to a csv file, or restore them from the csv file. For example to dump the keybindings you might use:

./keybindings.pl -e /tmp/keys.csv

and to restore them you might use:

./keybindings.pl -i /tmp/keys.csv

#!/usr/bin/perl

use strict;

my $action = '';
my $filename = '-';

for my $arg (@ARGV){
    if ($arg eq "-e" or $arg eq "--export"){
        $action = 'export';
    } elsif ($arg eq "-i" or $arg eq "--import"){
        $action = 'import';
    } elsif ($arg eq "-h" or $arg eq "--help"){
        print "Import and export keybindings\n";
        print " -e, --export <filename>\n";
        print " -i, --import <filename>\n";
        print " -h, --help\n";
        exit;
    } elsif ($arg =~ /^\-/){
        die "Unknown argument $arg";
    } else {
        $filename = $arg;
        if (!$action){
            if ( -e $filename){
                $action='import';
            } else {
                $action='export';
            }
        }
    }
}

$action='export' if (!$action);
if ($action eq 'export'){
    &export();
} else {
    &import();
}

sub export(){
    my $gsettingsFolders = [
        ['org.gnome.desktop.wm.keybindings','.'],
        ['org.gnome.settings-daemon.plugins.power','button'],
        ['org.gnome.settings-daemon.plugins.media-keys','.'],
    ];

    my $customBindings = [
    ];

    $filename = ">$filename";
    open (my $fh, $filename) || die "Can't open file $filename: $!";

    for my $folder (@$gsettingsFolders){
        my @keylist = split(/\n/, `gsettings list-recursively $folder->[0]`);
        foreach my $line (@keylist){
            if ($line =~ /^([^ ]+) ([^ ]+)(?: \@[a-z]+)? (.*)/){
                my ($path, $name, $value) = ($1,$2,$3);
                if ($name eq "custom-keybindings"){
                    $value =~ s/[\[\]\' ]//g;
                    my @c = split(/,/, $value);
                    $customBindings = \@c;
                } elsif ($name =~ /$folder->[1]/){
                    if ($value =~ /^\[|\'/){
                        if ($value =~ /^\[\'(?:disabled)?\'\]$/){
                            $value = '[]';
                        } 
                        print $fh "$path\t$name\t$value\n";
                    }
                }        
            } else {
                die "Could note parse $line";
            }
        }
    }   

    for my $folder (@$customBindings){
        my $gs = `gsettings list-recursively org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:$folder`;
        my ($binding) = $gs =~ /org.gnome.settings-daemon.plugins.media-keys.custom-keybinding binding (\'[^\n]+\')/g;
        my ($command) = $gs =~ /org.gnome.settings-daemon.plugins.media-keys.custom-keybinding command (\'[^\n]+\')/g;
        my ($name) = $gs =~ /org.gnome.settings-daemon.plugins.media-keys.custom-keybinding name (\'[^\n]+\')/g;
        $command =~ s/\"/\\\"/g;
        $command =~ s/^'(.*)'$/$1/g;
        $command =~ s/\'/\'\\\'\'/g;
        $command = "\'$command\'";
        print $fh "custom\t$name\t$command\t$binding\n"    
    }

    close($fh);
}

sub import(){

    $filename = "<$filename";
    open (my $fh, $filename) || die "Can't open file $filename: $!";

    my $customcount=0;

    while (my $line = <$fh>){
        chomp $line;
        if ($line){
            my @v = split(/\t/, $line);
            if (@v[0] eq 'custom'){
                my ($custom, $name, $command, $binding) = @v;
                print "Installing custom keybinding: $name\n";
                    print `gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom$customcount/ name \"$name\"`;
                print `gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom$customcount/ command \"$command\"`;
                print `gsettings set org.gnome.settings-daemon.plugins.media-keys.custom-keybinding:/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom$customcount/ binding \"$binding\"`;
                $customcount++;
            } else {
                my ($path, $name, $value) = @v;
                print "Importing $path $name\n";
                print `gsettings set \"$path\" \"$name\" \"$value\"`;
            }
        }       
    }
    if ($customcount > 0){
        my $customlist = "";
        for (my $i=0; $i<$customcount; $i++){
            $customlist .= "," if ($customlist);
            $customlist .= "'/org/gnome/settings-daemon/plugins/media-keys/custom-keybindings/custom$i/'";            
        }
        $customlist = "[$customlist]";
        print "Importing list of custom keybindings.\n";
        print `gsettings set org.gnome.settings-daemon.plugins.media-keys custom-keybindings \"$customlist\"`;
    }

    close($fh);
}

This includes the fix by user2589537 to allow custom commands with quotes in them.


How to backup dconf settings like keyboard shortcuts

  1. Dump them all to a file:

    dconf dump / > ~/.config/dconf/user.conf
    
  2. Open that file on a text editor and select the settings that you care about:

    editor ~/.config/dconf/user.conf
    

    If you use Vim, you will want this syntax highlight.

    You will soon ask yourself what is <Primary>.

  3. If you don't know the name of the setting, but know how to modify it from a GUI like unity-control-center, run:

    dconf watch /
    

    and then modify them. The exact setting will then appear on the terminal.

  4. When you want to restore those settings, run:

    dconf load / < ~/.config/dconf/user.conf
    
  5. Git track the config file to never lose it. Homeshick is my current favorite method.

Tested on Ubuntu 15.10. Tip adapted from here.

Gnome terminal is unfortunately resistant to such edits because of the unpredictable profile IDs.

Related: How can I restore default keyboard shortcuts?


The keyboard shortcuts are not stored in any one single place

Depending on your distro version, the keyboard shortcuts application may be called different things in the GUI but on the command line it should be called 'gnome-keybinding-properties'. It turns out that this neat little application brings all of the shortcuts into one convenient place for users to edit.

Since no one-click export/import tool exists for keybindings in Ubuntu, I wrote one in python:

Here's a link to the script

An export pulls the settings from the system. Essentially it contains a list of all the keybinding locations is hard-coded into a dictionary in the script. The script emumerates the list and runs the command "gconftool-2 --get [location]" for every key and stores the value in the dictionary along with the key. Then when you specify the location to save the settings, it saves the dictionary it to the file in json format.

An import script reverses this process. It loads the dictionary from the json file. Then it enumerates through these and runs the command "gconftool-2 --type string --set [location] [value]" to restore all of the saved settings.

One shot on either end and it saves/restores every single keyboard shortcut without touching any of the other system configuration settings.

The only dependencies for these scripts are:

  • python (tested and working in 2.6, 2.7, and 3.1)
  • gconftool-2 (comes standard with an Ubuntu install)
  • zenity (comes standard with an Ubuntu install)

Python is needed to execute the script, gconftool-2 is used to get/set the settings, zenity is needed for the file load/save gui dialogs.

I have created an entry on Ubuntu Brainstorm and am making progress on getting this feature introduced into the 'gnome-keybinding-properties' GUI menu.

Update:

I updated combined the import and export scripts into one, added full command line functionality (including --help info) and made it compatible for both python2x and python3k. Also, added the link to the Ubuntu Brainstorm idea.