702338bc7d
The IM can pretty much update the input sources anytime (even if to set the same ones). That ends up triggering rebuilding all user defined keymaps, and losing modifier state if we are unfortunate enough that this caught us while pressing one. One common situation seems to be password entries, resulting in the wrong character being printed if the first character happens to require the shift key. If the current keymap is not found in the newly loaded list, this._current will end up null, with the same behavior as we get currently (immediate keymap reload). https://bugzilla.redhat.com/show_bug.cgi?id=1569211 https://gitlab.gnome.org/GNOME/gnome-shell/issues/240 Closes: #240
159 lines
4.6 KiB
JavaScript
159 lines
4.6 KiB
JavaScript
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
|
|
const GLib = imports.gi.GLib;
|
|
const GnomeDesktop = imports.gi.GnomeDesktop;
|
|
const Lang = imports.lang;
|
|
const Meta = imports.gi.Meta;
|
|
|
|
const Main = imports.ui.main;
|
|
|
|
var DEFAULT_LOCALE = 'en_US';
|
|
var DEFAULT_LAYOUT = 'us';
|
|
var DEFAULT_VARIANT = '';
|
|
|
|
let _xkbInfo = null;
|
|
|
|
function getXkbInfo() {
|
|
if (_xkbInfo == null)
|
|
_xkbInfo = new GnomeDesktop.XkbInfo();
|
|
return _xkbInfo;
|
|
}
|
|
|
|
let _keyboardManager = null;
|
|
|
|
function getKeyboardManager() {
|
|
if (_keyboardManager == null)
|
|
_keyboardManager = new KeyboardManager();
|
|
return _keyboardManager;
|
|
}
|
|
|
|
function releaseKeyboard() {
|
|
if (Main.modalCount > 0)
|
|
global.display.unfreeze_keyboard(global.get_current_time());
|
|
else
|
|
global.display.ungrab_keyboard(global.get_current_time());
|
|
}
|
|
|
|
function holdKeyboard() {
|
|
global.display.freeze_keyboard(global.get_current_time());
|
|
}
|
|
|
|
var KeyboardManager = new Lang.Class({
|
|
Name: 'KeyboardManager',
|
|
|
|
// The XKB protocol doesn't allow for more that 4 layouts in a
|
|
// keymap. Wayland doesn't impose this limit and libxkbcommon can
|
|
// handle up to 32 layouts but since we need to support X clients
|
|
// even as a Wayland compositor, we can't bump this.
|
|
MAX_LAYOUTS_PER_GROUP: 4,
|
|
|
|
_init() {
|
|
this._xkbInfo = getXkbInfo();
|
|
this._current = null;
|
|
this._localeLayoutInfo = this._getLocaleLayout();
|
|
this._layoutInfos = {};
|
|
},
|
|
|
|
_applyLayoutGroup(group) {
|
|
let options = this._buildOptionsString();
|
|
let [layouts, variants] = this._buildGroupStrings(group);
|
|
Meta.get_backend().set_keymap(layouts, variants, options);
|
|
},
|
|
|
|
_applyLayoutGroupIndex(idx) {
|
|
Meta.get_backend().lock_layout_group(idx);
|
|
},
|
|
|
|
apply(id) {
|
|
let info = this._layoutInfos[id];
|
|
if (!info)
|
|
return;
|
|
|
|
if (this._current && this._current.group == info.group) {
|
|
if (this._current.groupIndex != info.groupIndex)
|
|
this._applyLayoutGroupIndex(info.groupIndex);
|
|
} else {
|
|
this._applyLayoutGroup(info.group);
|
|
this._applyLayoutGroupIndex(info.groupIndex);
|
|
}
|
|
|
|
this._current = info;
|
|
},
|
|
|
|
reapply() {
|
|
if (!this._current)
|
|
return;
|
|
|
|
this._applyLayoutGroup(this._current.group);
|
|
this._applyLayoutGroupIndex(this._current.groupIndex);
|
|
},
|
|
|
|
setUserLayouts(ids) {
|
|
let currentId = this._current ? this._current.id : null;
|
|
this._current = null;
|
|
this._layoutInfos = {};
|
|
|
|
for (let i = 0; i < ids.length; ++i) {
|
|
let [found, , , _layout, _variant] = this._xkbInfo.get_layout_info(ids[i]);
|
|
if (found)
|
|
this._layoutInfos[ids[i]] = { id: ids[i], layout: _layout, variant: _variant };
|
|
}
|
|
|
|
let i = 0;
|
|
let group = [];
|
|
for (let id in this._layoutInfos) {
|
|
// We need to leave one slot on each group free so that we
|
|
// can add a layout containing the symbols for the
|
|
// language used in UI strings to ensure that toolkits can
|
|
// handle mnemonics like Alt+Ф even if the user is
|
|
// actually typing in a different layout.
|
|
let groupIndex = i % (this.MAX_LAYOUTS_PER_GROUP - 1);
|
|
if (groupIndex == 0)
|
|
group = [];
|
|
|
|
let info = this._layoutInfos[id];
|
|
group[groupIndex] = info;
|
|
info.group = group;
|
|
info.groupIndex = groupIndex;
|
|
|
|
if (id == currentId)
|
|
this._current = info;
|
|
|
|
i += 1;
|
|
}
|
|
},
|
|
|
|
_getLocaleLayout() {
|
|
let locale = GLib.get_language_names()[0];
|
|
if (locale.indexOf('_') == -1)
|
|
locale = DEFAULT_LOCALE;
|
|
|
|
let [found, , id] = GnomeDesktop.get_input_source_from_locale(locale);
|
|
if (!found)
|
|
[, , id] = GnomeDesktop.get_input_source_from_locale(DEFAULT_LOCALE);
|
|
|
|
let _layout, _variant;
|
|
[found, , , _layout, _variant] = this._xkbInfo.get_layout_info(id);
|
|
if (found)
|
|
return { layout: _layout, variant: _variant };
|
|
else
|
|
return { layout: DEFAULT_LAYOUT, variant: DEFAULT_VARIANT };
|
|
},
|
|
|
|
_buildGroupStrings(_group) {
|
|
let group = _group.concat(this._localeLayoutInfo);
|
|
let layouts = group.map(g => g.layout).join(',');
|
|
let variants = group.map(g => g.variant).join(',');
|
|
return [layouts, variants];
|
|
},
|
|
|
|
setKeyboardOptions(options) {
|
|
this._xkbOptions = options;
|
|
},
|
|
|
|
_buildOptionsString() {
|
|
let options = this._xkbOptions.join(',');
|
|
return options;
|
|
}
|
|
});
|