I want to execute the following commands:
xmodmap -e "keycode 52=y"
xmodmap -e "keycode 29=z"
whenever I switch my keyboard layout (Shift+Alt). Is there any way to do that?
I want to execute the following commands:
xmodmap -e "keycode 52=y"
xmodmap -e "keycode 29=z"
whenever I switch my keyboard layout (Shift+Alt). Is there any way to do that?
By default, Ubuntu's input source is set in a database called gsettings
. Each setting has a specific schema associated with it. In particular, language switching is associated with org.gnome.desktop.input-sources
schema. That's the one we're interested in for all modern Ubuntu versions.
More specifically, we're interested in its key current
. When you change language via GUI, that key gets modified. There's many things you can do with it, from switching input language in command-line to defining shortcuts for each input method, even setting input method for each individual app can be done. One way to do so is via gsettings
command or dconf
command. These are external programs which live in your /usr/bin/
folder (this is the same method that Jacob's answer uses). Another way, is via specific set of utilities ( API's actually ) for Python programming language. This is the method I will show in my answer.
It should be noted that gsettings
doesn't always work. If you are using a non-standard input method, such as fcitx
for example, that might not be true. In fact sogou-pinyin ( Chinese input method ) uses something else known as dbus
, so approach with gsettings
won't work. But for simple case where you have default Ubuntu, gsettings
database is sufficient.
The way Jacob does it is via single run of external gsettings
command and modifying shortcuts, such that each time you click the shortcut, the program runs. There is another approach, via already existing Gio
API. This type of API would be used when you develop a proper desktop application for Ubuntu or other system that uses GNOME-related desktop. The script below illustrates the approach with Gio
API.
#!/usr/bin/env python
from __future__ import print_function
from gi.repository import Gio, GObject
import subprocess
def on_changed(settings, key):
# This will run if specific key of a schema changed
print("Language changed")
# Do something else here, for example call external program
subprocess.call(['xmodmap','-e', 'keycode 52=y'])
subprocess.call(['xmodmap','-e', 'keycode 29=z'])
def main():
# We're focusing on this specific schema
settings = Gio.Settings("org.gnome.desktop.input-sources")
# Once the "current" key changes, on-changed function will run
settings.connect("changed::current", on_changed)
loop = GObject.MainLoop()
loop.run()
if __name__ == "__main__":
main()
There are distinct advantages to this approach over interfering with the shortcuts:
There are some concerns in the comments from the other poster but while this is a persistent script also works in its favour. The meagre resources it does consume vastly outweigh the fact it'll eat a fraction of a percent of RAM.
gsettings
is extremely low on juice.
– Jacob Vlijm
Feb 09 '17 at 08:07
Gio
is an API specifically build for handling desktop events, which gsettings
command also uses, just in C language. Multiple professional projects for Linux desktop utilize it, from Red Hat to Ubuntu, and it is optimized for performance.
– Sergiy Kolodyazhnyy
Feb 09 '17 at 08:26
I am afraid switching sources is a built in function of Unity, which is not available as a cli option from outside. However, that does not mean we have no options to achieve exactly what you want.
Replacing the original shortcut
Input sources can be fetched by the command:
gsettings get org.gnome.desktop.input-sources sources
Output looks like:
[('xkb', 'us+intl'), ('xkb', 'us'), ('xkb', 'nl'), ('xkb', 'be')]
The layout can be set with the command:
gsettings set org.gnome.desktop.input-sources current <index>
This way, we can replace the keyboard shortcut by a scripted version to switch to the next language and run your command at the same time.
#!/usr/bin/env python3
import subprocess
import ast
import sys
arg = sys.argv[1]
key = "org.gnome.desktop.input-sources"
def get(cmd): return subprocess.check_output(cmd).decode("utf-8")
def run(cmd): subprocess.call(["/bin/bash", "-c", cmd])
langs = len(ast.literal_eval(get(["gsettings", "get", key, "sources"])))
currlang = int(get(["gsettings", "get", key, "current"]).split()[-1].strip())
if arg == "+":
next_lang = currlang+1 if currlang < langs-1 else 0
elif arg == "-":
next_lang = currlang-1 if currlang > 0 else langs-1
for cmd in [
"gsettings set "+key+" current "+str(next_lang),
'xmodmap -e "keycode 52=y"',
'xmodmap -e "keycode 29=z"',
]: run(cmd)
change_lang.py
Test- run the script by the command:
python3 /path/to/change_lang.py +
and optionally:
python3 /path/to/change_lang.py -
Your layout should change and your commands should run.
If all works fine, open System Settings, go to "Keyboard" > "Shortcuts" > "Typing". Click on the shortcut to switch source (on the right, to set the shortcut), and hit backspace to disable the shortcut.
Now add the same shortcut to custom shortcuts: choose: System Settings > "Keyboard" > "Shortcuts" > "Custom Shortcuts". Click the "+" and add the command:
python3 /path/to/change_lang.py +
To the shortcut you just disabled in step 2.
Additionally you can set a shortcut to move the other way around (set previous source from the list):
python3 /path/to/change_lang.py -
That's it. Now changing language is combined with the command you want to run along.
Whenever your shortcut is pressed, The script is called, looking what is the current language, by the command:
gsettings get org.gnome.desktop.input-sources current
The script subsequently moves to the next one, or the previous, depending on the argument, by the command:
gsettings set org.gnome.desktop.input-sources current <index>
Using gsettings
is extremely low on juice, and by combining the command to set the language with your commands, the script only runs when changing language.
I believe that is the most elegant way and, although you won't notice in your electricity bill, on long term the lowest on resources. The choice between a constantly running process, using the API, or a run-when-called option is a matter of taste however.
Note also that the script first changes language and then runs the other commands. There is no noticeable difference in response in the two answers whatsoever.
hue@hue-Lenovo-G580:~$ python3 ?/change_lang.py
Traceback (most recent call last):
File "?/change_lang.py", line 6, in
arg = sys.argv[1]
IndexError: list index out of range
I'm still relatively new to linux so I'm not exactly sure what to do now. Can you help me?
– huehuehuehuehuehuehuehuehuehue Feb 09 '17 at 18:43python3 /path/to/change_lang.py +
or python3 /path/to/change_lang.py -
(mind the + and -).
– Jacob Vlijm
Feb 09 '17 at 20:10
ast.literal_eval()
is to interpret the (string) output of the gsettings
command as a list, which is safer than using eval(). See https://docs.python.org/3/library/ast.html and https://docs.python.org/3/library/subprocess.html
– Jacob Vlijm
Feb 09 '17 at 22:52
The only annoying part is that I get confused when I switch back to the german keyboard to use letters like äüö, which makes it a bit harder to get used to the new layout. Also, for some very weird reason the alias keys="xmodmap -e "keycode 29=z"&&xmodmap -e "keycode 52=y"" isn't working either, but if I switch z and y it works...very strange.
– huehuehuehuehuehuehuehuehuehue Feb 12 '17 at 22:51
im-config -n
. – O8h7w Feb 08 '17 at 14:26But when I type the commands in regularly it works fine.
– huehuehuehuehuehuehuehuehuehue Feb 08 '17 at 16:37alias keys="xmodmap -e \"keycode 52=y\"&&xmodmap -e \"keycode 29=z\""
. And now mz kezs are crayz :D – O8h7w Feb 08 '17 at 17:35tail /var/log/Xorg.0.log
directly after doing a switch. – O8h7w Feb 08 '17 at 17:54im-config
, which is about input method frameworks for complex input methods such as IBus and Fcitx. – Gunnar Hjalmarsson Feb 08 '17 at 19:29