diff --git a/src/wm/keyboard.vala b/src/wm/keyboard.vala index 4419665..2128ece 100644 --- a/src/wm/keyboard.vala +++ b/src/wm/keyboard.vala @@ -1,331 +1,331 @@ /* * This file is part of budgie-desktop * * Copyright (C) 2015-2016 Ikey Doherty * Copyright (C) GNOME Shell Developers (Heavy inspiration, logic theft) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ namespace Budgie { public static const string DEFAULT_LOCALE = "en_US"; public static const string DEFAULT_LAYOUT = "us"; public static const string DEFAULT_VARIANT = ""; /* Default ibus engine to use */ public static const string DEFAULT_ENGINE = "xkb:us::eng"; errordomain InputMethodError { UNKNOWN_IME } class InputSource { public bool xkb = false; public string? layout = null; public string? variant = null; public uint idx = 0; public string? ibus_engine = null; public InputSource(Budgie.IBusManager? iman, string id, uint idx, string? layout, string? variant, bool xkb = false) throws Error { this.idx = idx; this.layout = layout; this.variant = variant; this.xkb = xkb; /* Attempt to fetch engine in the ibus daemon engine list */ var engine = iman.get_engine(id); if (engine == null) { if (!xkb) { throw new InputMethodError.UNKNOWN_IME("Unknown input method: id"); } return; } string? e_variant = engine.layout_variant; if (e_variant != null && e_variant.length > 0) { this.variant = e_variant; } this.layout = engine.layout; this.ibus_engine = id; } } public class KeyboardManager : GLib.Object { public unowned Budgie.BudgieWM? wm { construct set ; public get; } private Gnome.XkbInfo? xkb; string[] options = {}; Settings? settings = null; Array sources; InputSource fallback; uint current_source = 0; ulong sig_id = 0; /* Used to spawn and manage ibus */ IBusManager? ibus_manager; /* Guard ourselves from any future potential derps */ private bool is_keyboard_held = false; public KeyboardManager(Budgie.BudgieWM? wm) { Object(wm: wm); xkb = new Gnome.XkbInfo(); /* Only hook things up when ibus is setup, whether it failed or not */ ibus_manager = new IBusManager(this); ibus_manager.ready.connect(on_ibus_ready); ibus_manager.do_init(); } private void on_ibus_ready() { settings = new Settings("org.gnome.desktop.input-sources"); /* Special handling of the current source. */ sig_id = settings.changed["current"].connect(on_current_source_changed); settings.changed.connect(on_settings_changed); update_fallback(); on_settings_changed("xkb-options"); on_settings_changed("sources"); } public delegate void KeyHandlerFunc (Meta.Display display, Meta.Screen screen, Meta.Window? window, Clutter.KeyEvent? event, Meta.KeyBinding binding); void switch_input_source(Meta.Display display, Meta.Screen screen, Meta.Window? window, Clutter.KeyEvent? event, Meta.KeyBinding binding) { current_source = (current_source+1) % sources.length; this.hold_keyboard(); this.apply_layout(current_source); this.apply_ibus(); } void switch_input_source_backward(Meta.Display display, Meta.Screen screen, Meta.Window? window, Clutter.KeyEvent? event, Meta.KeyBinding binding) { current_source = (current_source-1) % sources.length; this.hold_keyboard(); this.apply_layout(current_source); this.apply_ibus(); } public void hook_extra() { var screen = wm.get_screen(); var display = screen.get_display(); /* Hook into GNOME defaults */ var schema = new Settings("org.gnome.desktop.wm.keybindings"); display.add_keybinding("switch-input-source", schema, Meta.KeyBindingFlags.NONE, switch_input_source); display.add_keybinding("switch-input-source-backward", schema, Meta.KeyBindingFlags.NONE, switch_input_source_backward); } void on_settings_changed(string key) { switch (key) { case "sources": /* Update our sources. */ update_sources(); break; case "xkb-options": /* Update our xkb-options */ this.options = settings.get_strv(key); break; default: return; } } /* Reset InputSource list and produce something consumable by xkb */ void update_sources() { sources = new Array(); var val = settings.get_value("sources"); for (size_t i = 0; i < val.n_children(); i++) { InputSource? source = null; string? id = null; string? type = null; val.get_child(i, "(ss)", out id, out type); if (id == "xkb") { string[] spl = type.split("+"); string? variant = ""; if (spl.length > 1) { variant = spl[1]; } source = new InputSource(this.ibus_manager, type, (uint)i, spl[0], variant, true); sources.append_val(source); } else { try { source = new InputSource(this.ibus_manager, type, (uint)i, null, null, false); sources.append_val(source); } catch (Error e) { message("Error adding source %s|%s: %s", id, type, e.message); } } } if (sources.length == 0) { /* Always add fallback last, at the very worst it's the only available * source and we use the locale guessed source */ fallback.idx = sources.length; sources.append_val(fallback); } this.hold_keyboard(); this.apply_layout_group(); /* Always start up with the last selected index if possible */ var default_idx = this.settings.get_uint("current"); this.apply_layout(default_idx); this.apply_ibus(); } /* Apply our given layout groups to mutter */ void apply_layout_group() { unowned InputSource? source; string[] layouts = {}; string[] variants = {}; for (uint i = 0; i < sources.length; i++) { source = sources.index(i); layouts += source.layout; variants += source.variant; } string? slayouts = string.joinv(",", layouts); string? svariants = string.joinv(",", variants); string? options = string.joinv(",", this.options); Meta.Backend.get_backend().set_keymap(slayouts, svariants, options); } /* Apply an indexed layout, i.e. 0 for now */ void apply_layout(uint idx) { if (idx > sources.length) { idx = 0; } this.current_source = idx; Meta.Backend.get_backend().lock_layout_group(idx); /* Send this off to gsettings so that clients know what our idx is */ this.write_source_index(idx); } void update_fallback() { string? type = null; string? id = null; string? locale = Intl.get_language_names()[0]; string? display_name = null; string? short_name = null; string? xkb_layout = null; string? xkb_variant = null; if (!locale.contains("_")) { locale = DEFAULT_LOCALE; } if (!Gnome.get_input_source_from_locale(locale, out type, out id)) { Gnome.get_input_source_from_locale(DEFAULT_LOCALE, out type, out id); } if(xkb.get_layout_info(id, out display_name, out short_name, out xkb_layout, out xkb_variant)) { fallback = new InputSource(this.ibus_manager, id, 0, xkb_layout, xkb_variant, true); } else { fallback = new InputSource(this.ibus_manager, id, 0, DEFAULT_LAYOUT, DEFAULT_VARIANT, true); } } /** * Update the index in gsettings so that clients know the current */ private void write_source_index(uint index) { SignalHandler.block(this.settings, this.sig_id); this.settings.set_uint("current", index); this.settings.apply(); SignalHandler.unblock(this.settings, this.sig_id); } /** * Someone else changed the current source, do somethin' about it */ private void on_current_source_changed() { uint new_source = this.settings.get_uint("current"); this.hold_keyboard(); apply_layout(new_source); this.apply_ibus(); } /** * Apply the ibus engine and then release the keyboard */ private void apply_ibus() { string engine_name; InputSource? current = sources.index(current_source); - if (current.ibus_engine != null) { + if (current != null && current.ibus_engine != null) { engine_name = current.ibus_engine; } else { engine_name = DEFAULT_ENGINE; } this.ibus_manager.set_engine(engine_name); } /** * Unfreeze the keyboard */ public void release_keyboard() { if (!is_keyboard_held) { return; } wm.get_screen().get_display().ungrab_keyboard(wm.get_screen().get_display().get_current_time()); is_keyboard_held = false; } /** * Freeze the keyboard so we don't loose input events */ public void hold_keyboard() { if (is_keyboard_held) { return; } wm.get_screen().get_display().freeze_keyboard(wm.get_screen().get_display().get_current_time()); is_keyboard_held = true; } } } /* End namespace */ /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 4 * tab-width: 4 * indent-tabs-mode: nil * End: * * vi: set shiftwidth=4 tabstop=4 expandtab: * :indentSize=4:tabSize=4:noTabs=true: */