udev-Regel zum automatischen Laden des Tastaturlayouts, wenn eine USB-Tastatur angeschlossen ist


24

Ich versuche, ein neues Tastaturlayout zu laden, wenn ich eine USB-Tastatur einstecke, aber meine Udev-Regel funktioniert nicht.

SUBSYSTEM == "input", ATTR {idVendor} == "062a", ATTR {idProduct} == "0201", GOTO = "usb_xmodmap_auto"

LABEL = "usb_xmodmap_auto"
ACTION == "add", RUN + = "/ usr / bin / xmodmap ~ / .usbXmodmap"
ACTION == "remove", RUN + = "/ usr / bin / xmodmap ~ / .pndXmodmap"

Ich habe die Regeln neu geladen mit:

> sudo udevadm control --reload-rules

und durch neustarten des systems aber beim einstecken der usb tastatur wird die orginal xmodmap noch geladen und somit ist das tastaturlayout falsch, aber wenn ich den befehl im terminal starte

> / usr / bin / xmodmap ~ / .usbXmodmap
oder
> / usr / bin / xmodmap ~ / .pndXmodmap

Sie funktionieren gut.

hoffe, jemand kann helfen.

Bearbeiten:

Um mehr zu helfen, habe ich einige Udevadm-Tests durchgeführt:

> udevadm test --action = add /devices/platform/ehci-omap.0/usb1/1-2/1-2.3/1-2.3:1.1/input/input10

Ausgänge:

run_command: Aufruf von: test
udevadm_test: Version 151
Dieses Programm ist nur zum Debuggen gedacht und führt kein Programm aus.
angegeben durch einen RUN-Schlüssel. Möglicherweise werden falsche Ergebnisse angezeigt, weil
Einige Werte können unterschiedlich sein oder bei einem Simulationslauf nicht verfügbar sein.

[...]
parse_file: Lesen von '/etc/udev/rules.d/usb-keyboard.rules' als Regeldatei
udev_rules_new: Regeln verwenden 100572 Bytes Token (8381 * 12 Bytes), 21523 Bytes Puffer
udev_rules_new: Temporärer Index verwendet 35380 Bytes (1769 * 20 Bytes)
udev_device_new_from_syspath: Gerät 0x3b4d8 hat den Pfad '/devices/platform/ehci-omap.0/usb1/1-2/1-2.3/1-2.3:1.1/input/input10'
udev_rules_apply_to_event: RUN '/ sbin / modprobe -b $ env {MODALIAS}' /etc/udev/rules.d/80-drivers.rules:5
udev_rules_apply_to_event: RUN 'socket: @ / org / freedesktop / hal / udev_event' /etc/udev/rules.d/90-hal.rules:2
udev_rules_apply_to_event: RUN '/ sbin / modprobe $ env {MODALIAS}' /etc/udev/rules.d/local.rules:31
udev_rules_apply_to_event: RUN 'socket: / org / kernel / udev / monitor' /etc/udev/rules.d/run.rules:2
udev_rules_apply_to_event: RUN '/ usr / bin / xmodmap ~ / .usbXmodmap' /etc/udev/rules.d/usb-keyboard.rules:4
udevadm_test: UDEV_LOG = 6
udevadm_test: DEVPATH = / devices / platform / ehci-omap.0 / usb1 / 1-2 / 1-2.3 / 1-2.3: 1.1 / input / input10
udevadm_test: PRODUCT = 3 / 62a / 201/110
udevadm_test: NAME = "USB-kompatible Tastatur"
udevadm_test: PHYS = "usb-ehci-omap.0-2.3 / input1"
udevadm_test: UNIQ = ""
udevadm_test: EV == 1f
udevadm_test: KEY == 837fff 2c3027 bf004444 0 0 1fe3 c04 a27c000 267bfa d941dfed 9e0000 0 0 0
udevadm_test: REL == 143
udevadm_test: ABS == 1 0
udevadm_test: MSC == 10
udevadm_test: MODALIAS = Eingabe: b0003v062Ap0201e0110-e0,1,2,3,4, k71,72,73,74,77,80,82,83,85,86,87,88,89,8A, 8B, 8C, 8E, 8F, 90, 96, 98, 9B, 9C, 9E, 9F, A1, A3, A4, A5, A6, A7, A8, A9, AB, AC, AD, AE, B1, B2, B5, CE, CF, D0, D1, D2, D5, D9, DB, E2, EA, EB, 100, 101, 105, 106, 107, 108, 109, 10B, 10C, 162, 166, 16A, 16E, 178, 179, 17A, 17B, 17C, 17D, 17F, 180, 181, 182, 185, 18C. 18D, 192,193,195,1A0,1A1,1A2,1A3,1A4,1A5,1A6,1A7,1A8,1A9,1AA, 1AB, 1AC, 1AD, 1AE, 1B0,1B1,1B7, r0,1,6,8, a20, m4, lsfw
udevadm_test: ACTION = add
udevadm_test: SUBSYSTEM = Eingabe
udevadm_test: run: '/ sbin / modprobe -b input: b0003v062Ap0201e0110-e0,1,2,3,4, k71,72,73,74,77,80,82,83,85,86,87,88,89 , 8A, 8B, 8C, 8E, 8F, 90, 96, 98, 9C, 9E, 9F, A1, A3, A4, A5, A6, A7, A8, A9, AB, AC, AD, AE, B1 , B2, B5, CE, CF, DO, D1, D2, D5, D9, DB, E2, EA, EB, 100, 101, 105, 106, 107, 109, 10A, 10C, 162, 166, 16A, 16E, 178, 179, 17A, 17B, 17C, 17D , 17F, 180,181,182,185,18C, 18D, 192,193,195,1A0,1A1,1A2,1A3,1A4,1A5,1A6,1A7,1A8,1A9,1AA, 1AB, 1AC, 1AD, 1AE, 1B0,1B1,1B7, r0,1 , 6,8, a20, m4, lsfw '
udevadm_test: run: 'socket: @ / org / freedesktop / hal / udev_event'
udevadm_test: run: '/ sbin / modprobe input: b0003v062Ap0201e0110-e0,1,2,3,4, k71,72,73,74,77,80,82,83,85,86,87,88,89,8A , 8B, 8C, 8E, 8F, 90, 96, 98, 9B, 9E, 9F, A1, A3, A4, A5, A6, A7, A8, A9, AB, AC, AD, AE, B1, B2 , B5, CE, CF, DO, D1, D2, D5, D9, DB, E2, EA, EB, 100, 101, 105, 106, 107, 108, 109, 10B, 10C, 162, 166, 16A, 16E, 178, 179, 17A, 17B, 17C, 17D, 17F , 180,181,182,185,18C, 18D, 192,193,195,1A0,1A1,1A2,1A3,1A4,1A5,1A6,1A7,1A8,1A9,1AA, 1AB, 1AC, 1AD, 1AE, 1B0,1B1,1B7, r0,1,6 , 8, a20, m4, lsfw '
udevadm_test: run: 'socket: / org / kernel / udev / monitor'
udevadm_test: run: '/ usr / bin / xmodmap ~ / .usbXmodmap'

und

> udevadm test --action = remove /devices/platform/ehci-omap.0/usb1/1-2/1-2.3/1-2.3:1.1/input/input10

Ausgänge:

run_command: Aufruf von: test
udevadm_test: Version 151
Dieses Programm ist nur zum Debuggen gedacht und führt kein Programm aus.
angegeben durch einen RUN-Schlüssel. Möglicherweise werden falsche Ergebnisse angezeigt, weil
Einige Werte können unterschiedlich sein oder bei einem Simulationslauf nicht verfügbar sein.

[...]
parse_file: Lesen von '/etc/udev/rules.d/usb-keyboard.rules' als Regeldatei
udev_rules_new: Regeln verwenden 100572 Bytes Token (8381 * 12 Bytes), 21523 Bytes Puffer
udev_rules_new: Temporärer Index verwendet 35380 Bytes (1769 * 20 Bytes)
udev_device_new_from_syspath: Gerät 0x3b4d8 hat den Pfad '/devices/platform/ehci-omap.0/usb1/1-2/1-2.3/1-2.3:1.1/input/input10'
udev_rules_apply_to_event: RUN 'socket: @ / org / freedesktop / hal / udev_event' /etc/udev/rules.d/90-hal.rules:2
udev_rules_apply_to_event: RUN 'socket: / org / kernel / udev / monitor' /etc/udev/rules.d/run.rules:2
udev_rules_apply_to_event: RUN '/ usr / bin / xmodmap ~ / .pndXmodmap' /etc/udev/rules.d/usb-keyboard.rules:5
udevadm_test: UDEV_LOG = 6
udevadm_test: DEVPATH = / devices / platform / ehci-omap.0 / usb1 / 1-2 / 1-2.3 / 1-2.3: 1.1 / input / input10
udevadm_test: PRODUCT = 3 / 62a / 201/110
udevadm_test: NAME = "USB-kompatible Tastatur"
udevadm_test: PHYS = "usb-ehci-omap.0-2.3 / input1"
udevadm_test: UNIQ = ""
udevadm_test: EV == 1f
udevadm_test: KEY == 837fff 2c3027 bf004444 0 0 1fe3 c04 a27c000 267bfa d941dfed 9e0000 0 0 0
udevadm_test: REL == 143
udevadm_test: ABS == 1 0
udevadm_test: MSC == 10
udevadm_test: MODALIAS = Eingabe: b0003v062Ap0201e0110-e0,1,2,3,4, k71,72,73,74,77,80,82,83,85,86,87,88,89,8A, 8B, 8C, 8E, 8F, 90, 96, 98, 9B, 9C, 9E, 9F, A1, A3, A4, A5, A6, A7, A8, A9, AB, AC, AD, AE, B1, B2, B5, CE, CF, D0, D1, D2, D5, D9, DB, E2, EA, EB, 100, 101, 105, 106, 107, 108, 109, 10B, 10C, 162, 166, 16A, 16E, 178, 179, 17A, 17B, 17C, 17D, 17F, 180, 181, 182, 185, 18C. 18D, 192,193,195,1A0,1A1,1A2,1A3,1A4,1A5,1A6,1A7,1A8,1A9,1AA, 1AB, 1AC, 1AD, 1AE, 1B0,1B1,1B7, r0,1,6,8, a20, m4, lsfw
udevadm_test: ACTION = entfernen
udevadm_test: SUBSYSTEM = Eingabe
udevadm_test: run: 'socket: @ / org / freedesktop / hal / udev_event'
udevadm_test: run: 'socket: / org / kernel / udev / monitor'
udevadm_test: run: '/ usr / bin / xmodmap ~ / .pndXmodmap'

was zu zeigen scheint, dass es funktionieren sollte, aber es hofft nicht, dass dies hilft, eine Antwort zu bekommen.


Antworten:


16

Ich habe einen Weg gefunden, das zu umgehen, obwohl es ein bisschen hackig ist.

Ich habe heute genau den gleichen Punkt erreicht, als ich versucht habe, zwei Tastaturen mit udev, setxkbmap und xinput -list einzurichten und mit USB-Hotplugging zu arbeiten. Ich tausche die Tasten aus, ohne das Layout zu ändern, aber es ist trotzdem so, wenn Sie Ihre Tastatur an einem Hotplug identifiziert haben und setxkbmap unter bestimmten Bedingungen aufrufen können, sollten Sie in der Lage sein, nur die Sprache der von Ihnen angegebenen Tastatur einzustellen . Die Liste der Tastaturlayouts finden Sie hier ls -l /usr/share/kbd/keymaps/i386/und Sie können Ihren Gerätenamen finden, mit dem Sie fortfahren möchten xinput -list.

  1. Sie sollten es durch rizumuIhren Benutzernamen ersetzen , da dies ohne ausdrückliche Angabe nicht möglich war.
  2. Stellen Sie sicher, dass Sie den yourTastaturnamen eingeben.
  3. Verwenden lsusbSie diese Option, um die Hardware-ID zu ermitteln, die Sie in der udev-Regel festlegen müssen. Meine Tastatur sieht so ausBus 002 Device 009: ID 04d9:2013 Holtek Semiconductor, Inc.

Ich habe zuerst die udev-Regel eingerichtet, um die Tastatur automatisch zu erkennen, indem ich eine udev-Regel erstelle:

In der Datei /etc/udev/rules.d/00-usb-keyboards.rules:

ACTION=="add", ATTRS{idVendor}=="04d9", ATTRS{idProduct}=="2013", RUN+="/home/rizumu/bin/kbd_udev", OWNER="rizumu"

Ich habe zwei Dateien ~ / bin / kbd und ~ / bin / kbd_udev. Stellen Sie sicher, dass sie die richtigen Berechtigungen habenchmod 755 ~/bin/kbd*

Das ~/bin/kbd_udevSkript enthält:

#!/bin/bash
/home/rizumu/bin/kbd &

Und Sie werden feststellen, dass es nur ~/bin/kbdim Hintergrund aufruft, damit udev den Vorgang abschließen und die Tastatur aktivieren kann. Innerhalb des ~/bin/kbdSkripts schlafen wir eine Sekunde lang, weil wir warten müssen, bis die Tastatur aktiviert ist, damit wir die Geräte-ID mithilfe von xinput abrufen können. Um achive dieses Ich habe einige Variablen gesetzt und exportiert sie so XInput setxkbmap kann thier Arbeit tun DISPLAY, XAUTHORITY, HOME, und eine daskb_idfür die ID meines daskeyboard:

#!/bin/bash
sleep 1
DISPLAY=":0.0"
HOME=/home/rizumu/
XAUTHORITY=$HOME/.Xauthority
export DISPLAY XAUTHORITY HOME
daskb_id=`xinput -list | grep -i 'daskeyboard' | grep -o id=[0-9]. | grep -o [0-9]. | head -1`

xset r rate 200 30
setxkbmap -layout colemak
setxkbmap -option ctrl:nocaps
if [ "${daskb_id}" ]; then
    setxkbmap -device "${daskb_id}" -option altwin:swap_lalt_lwin
fi

Vielen Dank für die Hilfe bei der Beantwortung meiner eigenen Frage ist AskUbuntu: askubuntu.com/questions/337411/…
Sadi

Und ich frage mich, ob Sie mir auch helfen können, eine Benachrichtigungsnachricht am Ende dieses Skripts hinzuzufügen (z notify-send "USB Keyboard is plugged in and ready for use now." -i gtk-dialog-info -t 1000 -u normal. B. ). Da ich nicht viel über Scripting weiß, habe ich versucht, es vor oder nach "fi" einzufügen, aber in beiden Fällen wurde die Benachrichtigung immer wieder angezeigt :-(
Sadi

Warum OWNERfür dieses Gerät einstellen ?
Limbo Peng

1
Was macht die xset r rate 200 30Leitung? xsetist auf meiner Ubuntu 17.04-Installation nicht verfügbar.
Kleinfreund

1
Ich kann kein xmodmap $HOME/.XmodmapSkript analog zu "/ home / rizumu / bin / kbd" ausführen. Warum sollte das so sein?
Geremia

5

Abhängig von Ihrer Distribution haben Sie möglicherweise bereits eine udev-Regel für Tastaturen in /lib/udev/rules.d/64-xorg-xkb.rules. Unter Ubuntu importiert diese Datei / etc / default / keyboard mit folgenden Optionen:

XKBMODEL="pc105"
XKBLAYOUT="us"
XKBVARIANT=""
XKBOPTIONS=""

Bei meinem Setup stellte ich fest, dass diese integrierte Regel nach meiner benutzerdefinierten udev-Regel ausgeführt wurde und meine Einstellungen überschrieb. Stattdessen habe ich XKBOPTIONS in / etc / default / keyboard folgendermaßen geändert:

XKBOPTIONS="-option ctrl:nocaps"

Um das Verhalten "Feststelltaste ist Steuerung" zu erhalten, wollte ich auf allen Tastaturen.


2
Großartige Idee! Meins funktioniert mit justXBKOPTIONS="ctrl:nocaps"
RasmusWL

3

Wenn Sie GNOME ausführen, müssen Sie das Tastaturverwaltungs-Plugin deaktivieren, damit Ihre Layoutänderungen nicht außer Kraft gesetzt werden.

gconftool-2 --toggle /apps/gnome_settings_daemon/plugins/keyboard/active

Führen Sie denselben Befehl erneut aus, um ihn wie gewünscht zu aktivieren.


Ich laufe Angström. Ob das funktioniert?
Jake Aitchison

Verwenden Sie GNOME auf Ångström?
Ignacio Vazquez-Abrams

Nein, ich benutze xfce 4.6.1
Jake Aitchison

1
In meinem Ubuntu 13.04 ist dies dconfunter /org/gnome/settings-daemon/plugins/keyboard/active.
nh2

1
Und der Befehl für Ubuntu 13.04 ist:gsettings set org.gnome.settings-daemon.plugins.keyboard active false
Sadi

3

Es funktioniert nicht, weil udevund xmodmapkeinen Zugriff auf Ihre X11 - Display haben. In der Tat, udevnicht einmal weiß , ob es ist aktiver X11 - Displays.

  • Hinweis: zeigt , Plural. Es kann nicht „den“ X11 - Display verwenden , weil es kann mehr als ein. Zum Beispiel, wenn Sie "schnelle Benutzerumschaltung" verwenden.

Wie könnte ich das zum Laufen bringen?
Jake Aitchison

weiß jemand, wie ich das beheben kann?
Jake Aitchison

1
Ich habe udev dazu gebracht, setxkbmap aufzurufen. Die udev-Regel ruft ein Skript auf, das ein anderes Skript hinterlegt (damit udev es vervollständigen kann). Das zweite Skript hält eine Sekunde lang an, richtet die erwarteten X11-Variablen ein und löst setxkbmap aus. Siehe meine Antwort auf die Hauptfrage für weitere Details.
Thomas Schreiber

@ Rizumu: Ahh, viel Glück, dass das mit GDM funktioniert.
Grawity

3

Ich denke, ich habe einen viel saubereren Weg gefunden, dies zu konfigurieren, der keinen speziellen X11-Hack erfordert.

Die Idee dahinter ist, dass udevnur neue Tastatureingaben erkannt werden und für jedes Layout ein Symlink erstellt wird. Anschließend inotifywird im Benutzerbereich nach neuen Layouts gesucht.

udev Regeln

#/etc/udev/rules.d/61-usb-keyboard-layout.rules

# will match my Logitech keyboard with US layout 
SUBSYSTEM=="input", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="c31c", GROUP="plugdev", MODE="0660", SYMLINK+="input/by-layout/us"

# will match my Lenovo integrated keyboard with IT layout
SUBSYSTEM=="input", ENV{ID_PATH}=="platform-i8042-serio-0", SYMLINK+="input/by-layout/it"

# force the directory to be recreated, just in case you unplug all input
SUBSYSTEM=="input", RUN="/bin/mkdir -p /dev/input/by-layout"

Mit diesen Regeln habe ich ein Verzeichnis unter dev ( /dev/input/by-layout), um nach Änderungen in den Userspace-Skripten zu suchen.

Userspace-Skript für KDE

Wenn ich beispielsweise KDE verwende, wird dieses Skript (automatisch) ausgeführt:

#!/bin/bash

# In case no link are found, switch to this layout
DEFAULT="it"

switch_layout () {
        [ ! -z "$1" ] || return 0
        /usr/bin/qdbus org.kde.keyboard /Layouts org.kde.KeyboardLayouts.setLayout $1
}

best_layout() {
        local LAYOUT=$(ls -1t /dev/input/by-layout/ | head -n 1)
        if [ -z "$LAYOUT" ] ; then
                LAYOUT=$DEFAULT
        fi
        echo $LAYOUT
}

switch_layout $(best_layout)

while true ; do
        EVENT=$(inotifywait -q -e create -e delete --exclude '.*tmp.*' /dev/input/by-layout/)

        if echo "$EVENT" | grep -qe CREATE ; then
                LAYOUT=${EVENT#?*CREATE }
        fi

        if echo "$EVENT" | grep -qe DELETE ; then
                LAYOUT=$(best_layout)
        fi

        switch_layout $LAYOUT
done

Das wirkt für mich wie ein Zauber. Um das Systemlayout zu ändern (was ich momentan nicht brauche), kann ein ähnliches Skript, das verwendet wird loadkeys, mit einem System-Init-Skript verteufelt werden.


Dank dessen wurde mir klar, dass ich inotifywaitbei jeder Änderung ein Setup-Skript ausführen kann /dev/input, da das Skript selbst idempotent ist.
Charlie Gorichanaz

3

Was ist mit der X.Org-Konfiguration? Aus Gentoo Wiki: X.Org/Input_drivers - udev :

Beispiel: Wenn Sie eine Logitech Access-Tastatur für die französische Schweiz besitzen, können Sie Folgendes verwenden:

Datei: /etc/X11/xorg.conf.d/10-keyboard.conf

Section "InputClass"
    Identifier             "evdev keyboard catchall"
    MatchIsKeyboard        "on"
    MatchDevicePath        "/dev/input/event*"
    Driver                 "evdev"
    Option                 "XkbModel" "logiaccess"
    Option                 "XkbLayout" "ch"
    Option                 "XkbVariant" "fr"
EndSection

Eine ausführliche Erklärung finden Sie unter:

man xorg.conf

und:

man evdev

ArchWiki demonstriert die Verwendung der gleichen Syntax in xorg.conf, merkt jedoch an, dass "heutzutage eine separate Konfigurationsdatei erstellt werden sollte, wie z. B. /etc/X11/xorg.conf.d/90-keyboard-layouts.conf". Ich verwende Arch und konfiguriere meine eigene USB-Tastatur in der vorhandenen /etc/X11/xorg.conf.d/vim 10-evdev.conf. Hat für mich funktioniert.

@ Rizumu: Clever kludge, danke fürs Teilen.


1
Ich habe kein x.org.conf.d-Verzeichnis unter Linux Mint 18.2
Max N

2

Um Ihre Frage zum Zugriff auf die aktuelle Anzeige zu beantworten, können Sie die entsprechende DISPLAY-Variable im Skript exportieren, sofern die Berechtigungen für die Anzeige korrekt festgelegt sind. ( man xsetFür Anzeigeberechtigungen.)

In vielen üblichen Fällen können Sie einfach export DISPLAY=:0den Befehl eingeben, da dies die erste Anzeige auf einem Einzelbenutzersystem ist. Es ist wahrscheinlich am einfachsten, ein Skript anstelle von xmodmap direkt auszuführen, da Sie so mehr Kontrolle über Umgebungsvariablen und den Rest haben. (Ersetzen Sie "/ usr / bin / xmodmap ~ / .usbXmodmap" in Ihrer Regel durch "/usr/local/bin/keyboard_plug.sh" und fügen Sie die entsprechenden Befehle zusammen mit der Variablen DISPLAY in dieses Skript ein.)

Wenn Sie jedoch DISPLAY =: 0 annehmen, können später Probleme auftreten, wenn Sie mehrere Benutzer oder Anzeigen haben. Sie können Skripte schreiben, um die entsprechende Anzeige zu erkennen. In diesem Fall sind Sie jedoch allein (was diese Antwort betrifft). :)


1

Da ich die Hacks nicht dazu bringen konnte, dass udev-Regeln funktionieren, schrieb ich ein kleines Python-Skript mit pyudev dem nach Eingabeereignissen gesucht habe.

#! /usr/bin/env python3

import pyudev
import time
import subprocess

ctx = pyudev.Context()
monitor = pyudev.Monitor.from_netlink(ctx)
monitor.filter_by("input")

def defer_xmodmap():
    time.sleep(1) # not sure if there's a race here, but it feels like there could be.
    subprocess.run("xmodmap ~/dotfiles/.xmodmap", shell=True)


for device in iter(monitor.poll, None):
    # there might be a way to add the action condition to the filter, but I couldn't find it
    if device.action != "add":
        continue

    # ensure the KB is initialized -- not sure if this is actually a needed check
    if not device.is_initialized:
        continue

    # my keyboard, from the output of `lsusb`
    if not "045E:07A5" in device.device_path:
        continue

    # it's the keyboard being added.
    defer_xmodmap()

Ich verwende dann diese systemd Benutzer-Unit-Datei, um es am Laufen zu halten (systemctl --user enable name_of_service_file ):

[Unit]
Description=udev xmodmap monitor

[Service]
ExecStart=/usr/bin/env python3 %h/local/bin/monitor_kb_udev
Restart=always
RestartSec=10

[Install]
WantedBy=default.target

Die inotifywaitLösung von @ giosh94mhz ist etwas einfacher und vermeidet die Abhängigkeit von pyudev. Aus irgendeinem Grund stellte ich jedoch fest, dass das inotifyEreignis 10 bis 20 Sekunden nach dem Anschließen meiner Tastatur nicht ausgelöst wurde.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.