Blame ui/gtk3/xkblayout.vala

Packit Service 1d8f1c
/* vim:set et sts=4 sw=4:
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * ibus - The Input Bus
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * Copyright(c) 2014 Red Hat, Inc.
Packit Service 1d8f1c
 * Copyright(c) 2014 Peng Huang <shawn.p.huang@gmail.com>
Packit Service 1d8f1c
 * Copyright(c) 2014 Takao Fujiwara <tfujiwar@redhat.com>
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * This library is free software; you can redistribute it and/or
Packit Service 1d8f1c
 * modify it under the terms of the GNU Lesser General Public
Packit Service 1d8f1c
 * License as published by the Free Software Foundation; either
Packit Service 1d8f1c
 * version 2.1 of the License, or (at your option) any later version.
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * This library is distributed in the hope that it will be useful,
Packit Service 1d8f1c
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 1d8f1c
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 1d8f1c
 * Lesser General Public License for more details.
Packit Service 1d8f1c
 *
Packit Service 1d8f1c
 * You should have received a copy of the GNU Lesser General Public
Packit Service 1d8f1c
 * License along with this library; if not, write to the Free Software
Packit Service 1d8f1c
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
Packit Service 1d8f1c
 * USA
Packit Service 1d8f1c
 */
Packit Service 1d8f1c
Packit Service 1d8f1c
class XKBLayout
Packit Service 1d8f1c
{
Packit Service 1d8f1c
    private const string XKB_COMMAND = "setxkbmap";
Packit Service 1d8f1c
    private const string XKB_QUERY_ARG = "-query";
Packit Service 1d8f1c
    private const string XKB_LAYOUT_ARG = "-layout";
Packit Service 1d8f1c
    private const string XMODMAP_COMMAND = "xmodmap";
Packit Service 1d8f1c
    private const string[] XMODMAP_KNOWN_FILES = {".xmodmap", ".xmodmaprc",
Packit Service 1d8f1c
                                                  ".Xmodmap", ".Xmodmaprc"};
Packit Service 1d8f1c
    private string[] m_xkb_latin_layouts = {};
Packit Service 1d8f1c
    private string m_default_layout = "";
Packit Service 1d8f1c
    private string m_default_variant = "";
Packit Service 1d8f1c
    private string m_default_option = "";
Packit Service 1d8f1c
    private bool m_use_xmodmap = true;
Packit Service 1d8f1c
Packit Service 1d8f1c
    public XKBLayout() {
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void set_latin_layouts(string[] xkb_latin_layouts) {
Packit Service 1d8f1c
        m_xkb_latin_layouts = xkb_latin_layouts;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public static void get_layout(out string layout,
Packit Service 1d8f1c
                                  out string variant,
Packit Service 1d8f1c
                                  out string option) {
Packit Service 1d8f1c
        string[] exec_command = {};
Packit Service 1d8f1c
        exec_command += XKB_COMMAND;
Packit Service 1d8f1c
        exec_command += XKB_QUERY_ARG;
Packit Service 1d8f1c
        string standard_output = null;
Packit Service 1d8f1c
        string standard_error = null;
Packit Service 1d8f1c
        int exit_status = 0;
Packit Service 1d8f1c
Packit Service 1d8f1c
        layout = "";
Packit Service 1d8f1c
        variant = "";
Packit Service 1d8f1c
        option = "";
Packit Service 1d8f1c
Packit Service 1d8f1c
        try {
Packit Service 1d8f1c
            GLib.Process.spawn_sync(null,
Packit Service 1d8f1c
                                    exec_command,
Packit Service 1d8f1c
                                    null,
Packit Service 1d8f1c
                                    GLib.SpawnFlags.SEARCH_PATH,
Packit Service 1d8f1c
                                    null,
Packit Service 1d8f1c
                                    out standard_output,
Packit Service 1d8f1c
                                    out standard_error,
Packit Service 1d8f1c
                                    out exit_status);
Packit Service 1d8f1c
        } catch (GLib.SpawnError err) {
Packit Service 1d8f1c
            stderr.printf("IBUS_ERROR: %s\n", err.message);
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
        if (exit_status != 0) {
Packit Service 1d8f1c
            stderr.printf("IBUS_ERROR: %s\n", standard_error ?? "");
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
        if (standard_output == null) {
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        foreach (string line in standard_output.split("\n")) {
Packit Service 1d8f1c
            string element = "layout:";
Packit Service 1d8f1c
            string retval = "";
Packit Service 1d8f1c
            if (line.has_prefix(element)) {
Packit Service 1d8f1c
                retval = line[element.length:line.length];
Packit Service 1d8f1c
                if (retval != null) {
Packit Service 1d8f1c
                    retval = retval.strip();
Packit Service 1d8f1c
                }
Packit Service 1d8f1c
                layout = retval;
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
Packit Service 1d8f1c
            element = "variant:";
Packit Service 1d8f1c
            retval = "";
Packit Service 1d8f1c
            if (line.has_prefix(element)) {
Packit Service 1d8f1c
                retval = line[element.length:line.length];
Packit Service 1d8f1c
                if (retval != null) {
Packit Service 1d8f1c
                    retval = retval.strip();
Packit Service 1d8f1c
                }
Packit Service 1d8f1c
                variant = retval;
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
Packit Service 1d8f1c
            element = "options:";
Packit Service 1d8f1c
            retval = "";
Packit Service 1d8f1c
            if (line.has_prefix(element)) {
Packit Service 1d8f1c
                retval = line[element.length:line.length];
Packit Service 1d8f1c
                if (retval != null) {
Packit Service 1d8f1c
                    retval = retval.strip();
Packit Service 1d8f1c
                }
Packit Service 1d8f1c
                option = retval;
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void set_layout(IBus.EngineDesc engine) {
Packit Service 1d8f1c
        string layout = engine.get_layout();
Packit Service 1d8f1c
        string variant = engine.get_layout_variant();
Packit Service 1d8f1c
        string option = engine.get_layout_option();
Packit Service 1d8f1c
Packit Service 1d8f1c
        assert (layout != null);
Packit Service 1d8f1c
Packit Service 1d8f1c
        /* If the layout is "default", return here so that the current
Packit Service 1d8f1c
         * keymap is not changed.
Packit Service 1d8f1c
         * Some engines do not wish to change the current keymap.
Packit Service 1d8f1c
         */
Packit Service 1d8f1c
        if (layout == "default" &&
Packit Service 1d8f1c
            (variant == "default" || variant == "") &&
Packit Service 1d8f1c
            (option == "default" || option == "")) {
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        bool need_us_layout = false;
Packit Service 1d8f1c
        if (variant != "eng")
Packit Service 1d8f1c
            need_us_layout = layout in m_xkb_latin_layouts;
Packit Service 1d8f1c
        if (!need_us_layout && variant != null)
Packit Service 1d8f1c
            need_us_layout =
Packit Service 1d8f1c
                    "%s(%s)".printf(layout, variant) in m_xkb_latin_layouts;
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (m_default_layout == "") {
Packit Service 1d8f1c
            get_layout (out m_default_layout,
Packit Service 1d8f1c
                        out m_default_variant,
Packit Service 1d8f1c
                        out m_default_option);
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (layout == "default" || layout == "") {
Packit Service 1d8f1c
            layout = m_default_layout;
Packit Service 1d8f1c
            variant = m_default_variant;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (layout == "") {
Packit Service 1d8f1c
            warning("Could not get the correct layout");
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (option == "default" || option == "") {
Packit Service 1d8f1c
            option = m_default_option;
Packit Service 1d8f1c
        } else {
Packit Service 1d8f1c
            if (!(option in m_default_option.split(","))) {
Packit Service 1d8f1c
                option = "%s,%s".printf(m_default_option, option);
Packit Service 1d8f1c
            } else {
Packit Service 1d8f1c
                option = m_default_option;
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (need_us_layout) {
Packit Service 1d8f1c
            layout += ",us";
Packit Service 1d8f1c
            if (variant != null) {
Packit Service 1d8f1c
                variant += ",";
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        string[] args = {};
Packit Service 1d8f1c
        args += XKB_COMMAND;
Packit Service 1d8f1c
        args += XKB_LAYOUT_ARG;
Packit Service 1d8f1c
        args += layout;
Packit Service 1d8f1c
        if (variant != null && variant != "" && variant != "default") {
Packit Service 1d8f1c
            args += "-variant";
Packit Service 1d8f1c
            args += variant;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
        if (option != null && option != "" && option != "default") {
Packit Service 1d8f1c
            /*TODO: Need to get the session XKB options */
Packit Service 1d8f1c
            args += "-option";
Packit Service 1d8f1c
            args += "-option";
Packit Service 1d8f1c
            args += option;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        string standard_error = null;
Packit Service 1d8f1c
        int exit_status = 0;
Packit Service 1d8f1c
        try {
Packit Service 1d8f1c
            if (!GLib.Process.spawn_sync(null,
Packit Service 1d8f1c
                                         args,
Packit Service 1d8f1c
                                         null,
Packit Service 1d8f1c
                                         GLib.SpawnFlags.SEARCH_PATH,
Packit Service 1d8f1c
                                         null,
Packit Service 1d8f1c
                                         null,
Packit Service 1d8f1c
                                         out standard_error,
Packit Service 1d8f1c
                                         out exit_status))
Packit Service 1d8f1c
                warning("Switch xkb layout to %s failed.",
Packit Service 1d8f1c
                        engine.get_layout());
Packit Service 1d8f1c
        } catch (GLib.SpawnError e) {
Packit Service 1d8f1c
            warning("Execute setxkbmap failed: %s", e.message);
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        if (exit_status != 0)
Packit Service 1d8f1c
            warning("Execute setxkbmap failed: %s",
Packit Service 1d8f1c
                    standard_error ?? "(null)");
Packit Service 1d8f1c
Packit Service 1d8f1c
        run_xmodmap();
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void run_xmodmap() {
Packit Service 1d8f1c
        if (!m_use_xmodmap) {
Packit Service 1d8f1c
            return;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
Packit Service 1d8f1c
        string homedir = GLib.Environment.get_home_dir();
Packit Service 1d8f1c
        foreach (string xmodmap_file in XMODMAP_KNOWN_FILES) {
Packit Service 1d8f1c
            string xmodmap_filepath = GLib.Path.build_filename(homedir,
Packit Service 1d8f1c
                                                               xmodmap_file);
Packit Service 1d8f1c
Packit Service 1d8f1c
            if (!GLib.FileUtils.test(xmodmap_filepath, GLib.FileTest.EXISTS)) {
Packit Service 1d8f1c
                continue;
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
Packit Service 1d8f1c
            string[] args = {XMODMAP_COMMAND, xmodmap_filepath};
Packit Service 1d8f1c
Packit Service 1d8f1c
            /* Call async here because if both setxkbmap and xmodmap is
Packit Service 1d8f1c
             * sync, it seems a DBus timeout happens and xmodmap causes
Packit Service 1d8f1c
             * a loop for a while and users would think panel icon is
Packit Service 1d8f1c
             * frozen in case the global engine mode is disabled.
Packit Service 1d8f1c
             *
Packit Service 1d8f1c
             * Do not return here even if the previous async is running
Packit Service 1d8f1c
             * so that all xmodmap can be done after setxkbmap is called.
Packit Service 1d8f1c
             */
Packit Service 1d8f1c
            try {
Packit Service 1d8f1c
                GLib.Process.spawn_async(null,
Packit Service 1d8f1c
                                         args,
Packit Service 1d8f1c
                                         null,
Packit Service 1d8f1c
                                         GLib.SpawnFlags.SEARCH_PATH,
Packit Service 1d8f1c
                                         null,
Packit Service 1d8f1c
                                         null);
Packit Service 1d8f1c
            } catch (GLib.SpawnError e) {
Packit Service 1d8f1c
                warning("Execute xmodmap is failed: %s\n", e.message);
Packit Service 1d8f1c
                return;
Packit Service 1d8f1c
            }
Packit Service 1d8f1c
Packit Service 1d8f1c
            break;
Packit Service 1d8f1c
        }
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
Packit Service 1d8f1c
    public void set_use_xmodmap(bool use_xmodmap) {
Packit Service 1d8f1c
        m_use_xmodmap = use_xmodmap;
Packit Service 1d8f1c
    }
Packit Service 1d8f1c
}