Adapt to keyboard layout API changes
Resolves: RHEL-106779
This commit is contained in:
parent
e8e9624acd
commit
3f21ead5da
@ -0,0 +1,30 @@
|
||||
From 0aa8ffb402fab4b0841540f271ab0401b1185907 Mon Sep 17 00:00:00 2001
|
||||
From: Carlos Garnacho <carlosg@gnome.org>
|
||||
Date: Thu, 5 Feb 2026 22:56:47 +0100
|
||||
Subject: [PATCH] environment: Drop Meta.Backend.set_keymap_layout_group_async
|
||||
promisify
|
||||
|
||||
This method was removed in mutter, its async promisification is not
|
||||
necessary anymore.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/4068>
|
||||
(cherry picked from commit ae7917c904dd9d80982e6e353b4bddc7420a4a98)
|
||||
---
|
||||
js/ui/environment.js | 1 -
|
||||
1 file changed, 1 deletion(-)
|
||||
|
||||
diff --git a/js/ui/environment.js b/js/ui/environment.js
|
||||
index f2dc69eef7..3c9d117f17 100644
|
||||
--- a/js/ui/environment.js
|
||||
+++ b/js/ui/environment.js
|
||||
@@ -33,7 +33,6 @@ Gio._promisify(Gio.File.prototype, 'query_info_async');
|
||||
Gio._promisify(Polkit.Permission, 'new');
|
||||
Gio._promisify(Shell.App.prototype, 'activate_action');
|
||||
Gio._promisify(Meta.Backend.prototype, 'set_keymap_async');
|
||||
-Gio._promisify(Meta.Backend.prototype, 'set_keymap_layout_group_async');
|
||||
|
||||
// We can't import shell JS modules yet, because they may have
|
||||
// variable initializations, etc, that depend on this file's
|
||||
--
|
||||
2.54.0
|
||||
|
||||
428
0001-status-keyboard-Adapt-to-external-source-of-keyboard.patch
Normal file
428
0001-status-keyboard-Adapt-to-external-source-of-keyboard.patch
Normal file
@ -0,0 +1,428 @@
|
||||
From 36ba4b88b3739b4862022f62aeddc9ae8c757bd8 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Jonas=20=C3=85dahl?= <jadahl@gmail.com>
|
||||
Date: Fri, 3 Oct 2025 17:02:37 +0200
|
||||
Subject: [PATCH] status/keyboard: Adapt to external source of keyboard layout
|
||||
|
||||
This adapts to API changes that allow an external source (e.g. remote
|
||||
desktop API user) to control the current keyboard layout. This is done
|
||||
in two ways:
|
||||
|
||||
If the external keyboard source sets the keymap as "locked", the
|
||||
indicator is fixed to the short name of the external source, and will
|
||||
change the indicator label when the layout index is changed accordingly.
|
||||
The builtin input sources are not selectable, and the keyboard selection
|
||||
keybinding is disabled.
|
||||
|
||||
If the external keyboard source doesn't set the keymap as "locked",
|
||||
whenever the current keymap is from an external source, it's indicated
|
||||
as a non-sensitive but selected input source. Selecting another keyboard
|
||||
layout overrides the one selected by the external entity.
|
||||
|
||||
Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/3901>
|
||||
(cherry picked from commit 0f90346872477d91a3e0be605a55bca1d163d735)
|
||||
---
|
||||
js/misc/keyboardManager.js | 122 ++++++++++++++++++++++++++-------
|
||||
js/ui/status/keyboard.js | 135 +++++++++++++++++++++++++++++--------
|
||||
2 files changed, 206 insertions(+), 51 deletions(-)
|
||||
|
||||
diff --git a/js/misc/keyboardManager.js b/js/misc/keyboardManager.js
|
||||
index 173d2a7c13..4185c61667 100644
|
||||
--- a/js/misc/keyboardManager.js
|
||||
+++ b/js/misc/keyboardManager.js
|
||||
@@ -1,5 +1,8 @@
|
||||
import GLib from 'gi://GLib';
|
||||
import GnomeDesktop from 'gi://GnomeDesktop';
|
||||
+import Meta from 'gi://Meta';
|
||||
+
|
||||
+import * as Signals from './signals.js';
|
||||
|
||||
import * as Main from '../ui/main.js';
|
||||
|
||||
@@ -40,8 +43,10 @@ export function holdKeyboard() {
|
||||
global.backend.freeze_keyboard(global.get_current_time());
|
||||
}
|
||||
|
||||
-class KeyboardManager {
|
||||
+class KeyboardManager extends Signals.EventEmitter {
|
||||
constructor() {
|
||||
+ super();
|
||||
+
|
||||
// The XKB protocol doesn't allow for more than 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
|
||||
@@ -53,48 +58,87 @@ class KeyboardManager {
|
||||
this._localeLayoutInfo = this._getLocaleLayout();
|
||||
this._layoutInfos = {};
|
||||
this._currentKeymap = null;
|
||||
+
|
||||
+ global.backend.connect('keymap-changed', this._onKeymapChanged.bind(this));
|
||||
+ global.backend.connect('keymap-layout-group-changed', this._onKeymapLayoutGroupChanged.bind(this));
|
||||
+ global.backend.connect('reset-keymap-description',
|
||||
+ () => this._ourKeymapDescription);
|
||||
+ global.backend.connect('reset-keymap-layout-index',
|
||||
+ () => this._current.groupIndex);
|
||||
}
|
||||
|
||||
- async _applyLayoutGroup(group) {
|
||||
- let options = this._buildOptionsString();
|
||||
- let [layouts, variants] = this._buildGroupStrings(group);
|
||||
- let model = this._xkbModel;
|
||||
+ _updateCurrentKeymap(info) {
|
||||
+ const options = this._buildOptionsString();
|
||||
+ const [layouts, variants] = this._buildGroupStrings(info.group);
|
||||
+ const model = this._xkbModel;
|
||||
|
||||
if (this._currentKeymap &&
|
||||
this._currentKeymap.layouts === layouts &&
|
||||
this._currentKeymap.variants === variants &&
|
||||
this._currentKeymap.options === options &&
|
||||
this._currentKeymap.model === model)
|
||||
- return;
|
||||
+ return false;
|
||||
+
|
||||
+ const displayNames = info.group.map(g => g.displayName);
|
||||
+ const shortNames = info.group.map(g => g.shortName);
|
||||
+ this._currentKeymap = {
|
||||
+ layouts,
|
||||
+ variants,
|
||||
+ options,
|
||||
+ model,
|
||||
+ displayNames,
|
||||
+ shortNames,
|
||||
+ };
|
||||
+ return true;
|
||||
+ }
|
||||
|
||||
- this._currentKeymap = {layouts, variants, options, model};
|
||||
- await global.backend.set_keymap_async(layouts, variants, options, model, null);
|
||||
+ _createKeymapDescription() {
|
||||
+ return Meta.KeymapDescription.new_from_rules(this._currentKeymap.model,
|
||||
+ this._currentKeymap.layouts,
|
||||
+ this._currentKeymap.variants,
|
||||
+ this._currentKeymap.options,
|
||||
+ this._currentKeymap.displayNames,
|
||||
+ this._currentKeymap.shortNames
|
||||
+ );
|
||||
}
|
||||
|
||||
- async _applyLayoutGroupIndex(idx) {
|
||||
- await global.backend.set_keymap_layout_group_async(idx, null);
|
||||
+ _onKeymapChanged() {
|
||||
+ this._keymapDescription = global.backend.get_keymap_description();
|
||||
+ this.emit('keymap-changed');
|
||||
}
|
||||
|
||||
- async _doApply(info) {
|
||||
- await this._applyLayoutGroup(info.group);
|
||||
- await this._applyLayoutGroupIndex(info.groupIndex);
|
||||
+ _onKeymapLayoutGroupChanged() {
|
||||
+ this.emit('keymap-changed');
|
||||
}
|
||||
|
||||
- apply(id) {
|
||||
- let info = this._layoutInfos[id];
|
||||
+ async _doApply(id) {
|
||||
+ const 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).catch(logError);
|
||||
- } else {
|
||||
- this._doApply(info).catch(logError);
|
||||
+ let recreate;
|
||||
+ if (this._updateCurrentKeymap(info))
|
||||
+ recreate = true;
|
||||
+ else if (this.isExternal())
|
||||
+ recreate = true;
|
||||
+ else
|
||||
+ recreate = false;
|
||||
+
|
||||
+ if (recreate)
|
||||
+ this._ourKeymapDescription = this._createKeymapDescription();
|
||||
+
|
||||
+ if (recreate || !this._current || this._current.groupIndex !== info.groupIndex) {
|
||||
+ await global.backend.set_keymap_async(
|
||||
+ this._ourKeymapDescription, info.groupIndex, null);
|
||||
}
|
||||
|
||||
this._current = info;
|
||||
}
|
||||
|
||||
+ apply(id) {
|
||||
+ this._doApply(id).catch(logError);
|
||||
+ }
|
||||
+
|
||||
reapply() {
|
||||
if (!this._current)
|
||||
return;
|
||||
@@ -107,9 +151,17 @@ class KeyboardManager {
|
||||
this._layoutInfos = {};
|
||||
|
||||
for (const id of ids) {
|
||||
- let [found, , , layout, variant] = this._xkbInfo.get_layout_info(id);
|
||||
- if (found)
|
||||
- this._layoutInfos[id] = {id, layout, variant};
|
||||
+ const [found, displayName, shortName, layout, variant] =
|
||||
+ this._xkbInfo.get_layout_info(id);
|
||||
+ if (found) {
|
||||
+ this._layoutInfos[id] = {
|
||||
+ id,
|
||||
+ layout,
|
||||
+ variant,
|
||||
+ displayName,
|
||||
+ shortName,
|
||||
+ };
|
||||
+ }
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
@@ -173,4 +225,28 @@ class KeyboardManager {
|
||||
get currentLayout() {
|
||||
return this._current;
|
||||
}
|
||||
+
|
||||
+ get shortName() {
|
||||
+ const seat = global.stage.context.get_backend().get_default_seat();
|
||||
+ const keymap = seat.get_keymap();
|
||||
+ return keymap.get_current_short_name();
|
||||
+ }
|
||||
+
|
||||
+ get displayName() {
|
||||
+ const seat = global.stage.context.get_backend().get_default_seat();
|
||||
+ const keymap = seat.get_keymap();
|
||||
+ return keymap.get_current_display_name();
|
||||
+ }
|
||||
+
|
||||
+ isLocked() {
|
||||
+ return this._keymapDescription?.is_locked() ?? false;
|
||||
+ }
|
||||
+
|
||||
+ isExternal() {
|
||||
+ if (!this._keymapDescription)
|
||||
+ return false;
|
||||
+ if (!this._ourKeymapDescription)
|
||||
+ return true;
|
||||
+ return !this._keymapDescription.direct_equal(this._ourKeymapDescription);
|
||||
+ }
|
||||
}
|
||||
diff --git a/js/ui/status/keyboard.js b/js/ui/status/keyboard.js
|
||||
index 9245dbeed4..d9a8b9d6dc 100644
|
||||
--- a/js/ui/status/keyboard.js
|
||||
+++ b/js/ui/status/keyboard.js
|
||||
@@ -25,8 +25,6 @@ class LayoutMenuItem extends PopupMenu.PopupBaseMenuItem {
|
||||
_init(displayName, shortName) {
|
||||
super._init();
|
||||
|
||||
- this.setOrnament(PopupMenu.Ornament.NO_DOT);
|
||||
-
|
||||
this.label = new St.Label({
|
||||
text: displayName,
|
||||
x_expand: true,
|
||||
@@ -371,6 +369,7 @@ export class InputSourceManager extends Signals.EventEmitter {
|
||||
|
||||
this._xkbInfo = KeyboardManager.getXkbInfo();
|
||||
this._keyboardManager = KeyboardManager.getKeyboardManager();
|
||||
+ this._keyboardManager.connect('keymap-changed', this._keymapChanged.bind(this));
|
||||
|
||||
this._ibusReady = false;
|
||||
this._ibusManager = IBusManager.getIBusManager();
|
||||
@@ -429,6 +428,9 @@ export class InputSourceManager extends Signals.EventEmitter {
|
||||
}
|
||||
|
||||
_switchInputSource(display, window, event, binding) {
|
||||
+ if (this._keyboardManager.isLocked())
|
||||
+ return;
|
||||
+
|
||||
if (this._mruSources.length < 2)
|
||||
return;
|
||||
|
||||
@@ -462,6 +464,10 @@ export class InputSourceManager extends Signals.EventEmitter {
|
||||
this._keyboardManager.reapply();
|
||||
}
|
||||
|
||||
+ _keymapChanged() {
|
||||
+ this.emit('keymap-changed');
|
||||
+ }
|
||||
+
|
||||
_updateMruSettings() {
|
||||
// If IBus is not ready we don't have a full picture of all
|
||||
// the available sources, so don't update the setting
|
||||
@@ -912,7 +918,8 @@ class InputSourceIndicator extends PanelMenu.Button {
|
||||
this._inputSourceManager = getInputSourceManager();
|
||||
this._inputSourceManager.connectObject(
|
||||
'sources-changed', this._sourcesChanged.bind(this),
|
||||
- 'current-source-changed', this._currentSourceChanged.bind(this), this);
|
||||
+ 'current-source-changed', this._currentSourceChanged.bind(this),
|
||||
+ 'keymap-changed', this._keymapChanged.bind(this), this);
|
||||
this._inputSourceManager.reload();
|
||||
}
|
||||
|
||||
@@ -928,47 +935,114 @@ class InputSourceIndicator extends PanelMenu.Button {
|
||||
this._showLayoutItem.visible = Main.sessionMode.allowSettings;
|
||||
}
|
||||
|
||||
- _sourcesChanged() {
|
||||
- for (let i in this._menuItems)
|
||||
- this._menuItems[i].destroy();
|
||||
- for (let i in this._indicatorLabels)
|
||||
- this._indicatorLabels[i].destroy();
|
||||
+ _createExternalSource(keyboardManager) {
|
||||
+ const {displayName, shortName} = keyboardManager;
|
||||
+ const is = new InputSource('external', 'external', displayName, shortName, -1);
|
||||
+ is.locked = keyboardManager.isLocked();
|
||||
+ return is;
|
||||
+ }
|
||||
|
||||
- this._menuItems = {};
|
||||
- this._indicatorLabels = {};
|
||||
+ _getCurrentSource() {
|
||||
+ const {keyboardManager} = this._inputSourceManager;
|
||||
+ if (keyboardManager.isExternal())
|
||||
+ return this._inputSources[0];
|
||||
+ else
|
||||
+ return this._inputSourceManager.currentSource;
|
||||
+ }
|
||||
+
|
||||
+ _updateInputSources() {
|
||||
+ const {keyboardManager} = this._inputSourceManager;
|
||||
+ if (keyboardManager.isLocked()) {
|
||||
+ const is = this._createExternalSource(keyboardManager);
|
||||
+ this._inputSources = [is];
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ const inputSources = [];
|
||||
+ if (keyboardManager.isExternal())
|
||||
+ inputSources.push(this._createExternalSource(keyboardManager));
|
||||
+
|
||||
+ const internalSources = this._inputSourceManager.inputSources;
|
||||
+ inputSources.push(
|
||||
+ ...Object.keys(internalSources).sort((a, b) => a - b).map(i => internalSources[i]));
|
||||
+
|
||||
+ this._inputSources = inputSources;
|
||||
+ }
|
||||
+
|
||||
+ _addSourceIndicators() {
|
||||
+ const inputSources = this._inputSources;
|
||||
+ for (const i in inputSources) {
|
||||
+ const is = inputSources[i];
|
||||
|
||||
- let menuIndex = 0;
|
||||
- for (let i in this._inputSourceManager.inputSources) {
|
||||
- let is = this._inputSourceManager.inputSources[i];
|
||||
+ const menuItem = new LayoutMenuItem(is.displayName, is.shortName);
|
||||
+ if (is.type !== 'external')
|
||||
+ menuItem.connect('activate', () => is.activate(true));
|
||||
+ else
|
||||
+ menuItem.sensitive = false;
|
||||
|
||||
- let menuItem = new LayoutMenuItem(is.displayName, is.shortName);
|
||||
- menuItem.connect('activate', () => is.activate(true));
|
||||
+ if (is.locked)
|
||||
+ menuItem.setOrnament(PopupMenu.Ornament.HIDDEN);
|
||||
+ else
|
||||
+ menuItem.setOrnament(PopupMenu.Ornament.NO_DOT);
|
||||
|
||||
const indicatorLabel = new St.Label({
|
||||
text: is.shortName,
|
||||
visible: false,
|
||||
});
|
||||
|
||||
- this._menuItems[i] = menuItem;
|
||||
- this._indicatorLabels[i] = indicatorLabel;
|
||||
+ const indicatorIndex = this._calculateSourceIndicatorIndex(is);
|
||||
+
|
||||
+ this._menuItems[indicatorIndex] = menuItem;
|
||||
+ this._indicatorLabels[indicatorIndex] = indicatorLabel;
|
||||
is.connect('changed', () => {
|
||||
menuItem.indicator.set_text(is.shortName);
|
||||
indicatorLabel.set_text(is.shortName);
|
||||
});
|
||||
|
||||
- this.menu.addMenuItem(menuItem, menuIndex++);
|
||||
+ this.menu.addMenuItem(menuItem, indicatorIndex);
|
||||
this._container.add_child(indicatorLabel);
|
||||
}
|
||||
}
|
||||
|
||||
+ _sourcesChanged() {
|
||||
+ this._updateInputSources();
|
||||
+
|
||||
+ for (const i in this._menuItems)
|
||||
+ this._menuItems[i].destroy();
|
||||
+ for (let i in this._indicatorLabels)
|
||||
+ this._indicatorLabels[i].destroy();
|
||||
+
|
||||
+ this._menuItems = {};
|
||||
+ this._indicatorLabels = {};
|
||||
+
|
||||
+ this._addSourceIndicators();
|
||||
+ }
|
||||
+
|
||||
+ _calculateSourceIndicatorIndex(source) {
|
||||
+ const keyboardManager = this._inputSourceManager.keyboardManager;
|
||||
+ return (keyboardManager.isExternal() ? 1 : 0) + source.index;
|
||||
+ }
|
||||
+
|
||||
+ _setSourceAsActive(source) {
|
||||
+ const index = this._calculateSourceIndicatorIndex(source);
|
||||
+ if (!source.locked)
|
||||
+ this._menuItems[index]?.setOrnament(PopupMenu.Ornament.DOT);
|
||||
+ this._indicatorLabels[index]?.show();
|
||||
+ }
|
||||
+
|
||||
+ _setSourceAsInactive(source) {
|
||||
+ const index = this._calculateSourceIndicatorIndex(source);
|
||||
+ if (!source.locked)
|
||||
+ this._menuItems[index]?.setOrnament(PopupMenu.Ornament.NO_DOT);
|
||||
+ this._indicatorLabels[index].hide();
|
||||
+ }
|
||||
+
|
||||
_currentSourceChanged(manager, oldSource) {
|
||||
- let nVisibleSources = Object.keys(this._inputSourceManager.inputSources).length;
|
||||
- let newSource = this._inputSourceManager.currentSource;
|
||||
+ const nVisibleSources = Object.keys(this._inputSources).length;
|
||||
+ const newSource = this._getCurrentSource();
|
||||
|
||||
- if (oldSource) {
|
||||
- this._menuItems[oldSource.index].setOrnament(PopupMenu.Ornament.NO_DOT);
|
||||
- this._indicatorLabels[oldSource.index].hide();
|
||||
- }
|
||||
+ if (oldSource)
|
||||
+ this._setSourceAsInactive(oldSource);
|
||||
|
||||
if (!newSource || (nVisibleSources < 2 && !newSource.properties)) {
|
||||
// This source index might be invalid if we weren't able
|
||||
@@ -986,8 +1060,12 @@ class InputSourceIndicator extends PanelMenu.Button {
|
||||
|
||||
this._buildPropSection(newSource.properties);
|
||||
|
||||
- this._menuItems[newSource.index].setOrnament(PopupMenu.Ornament.DOT);
|
||||
- this._indicatorLabels[newSource.index].show();
|
||||
+ this._setSourceAsActive(newSource);
|
||||
+ }
|
||||
+
|
||||
+ _keymapChanged() {
|
||||
+ this._sourcesChanged();
|
||||
+ this._setSourceAsActive(this._getCurrentSource());
|
||||
}
|
||||
|
||||
_buildPropSection(properties) {
|
||||
@@ -1023,9 +1101,10 @@ class InputSourceIndicator extends PanelMenu.Button {
|
||||
else
|
||||
text = prop.get_label().get_text();
|
||||
|
||||
- let currentSource = this._inputSourceManager.currentSource;
|
||||
+ const currentSource = this._getCurrentSource();
|
||||
if (currentSource) {
|
||||
- let indicatorLabel = this._indicatorLabels[currentSource.index];
|
||||
+ const index = this._calculateSourceIndicatorIndex(currentSource);
|
||||
+ const indicatorLabel = this._indicatorLabels[index];
|
||||
if (text && text.length > 0 && text.length < 3)
|
||||
indicatorLabel.set_text(text);
|
||||
}
|
||||
--
|
||||
2.54.0
|
||||
|
||||
@ -61,6 +61,10 @@ Patch: screenshot-tool.patch
|
||||
Patch: 0001-Revert-status-keyboard-Limit-the-input-method-indica.patch
|
||||
Patch: 0001-Revert-Require-gjs-1.81.2-for-build-because-Intl.Seg.patch
|
||||
|
||||
# Adapt to keyboard layout API changes (RHEL-106779)
|
||||
Patch: 0001-status-keyboard-Adapt-to-external-source-of-keyboard.patch
|
||||
Patch: 0001-environment-Drop-Meta.Backend.set_keymap_layout_grou.patch
|
||||
|
||||
%define eds_version 3.45.1
|
||||
%define gnome_desktop_version 44.0-7
|
||||
%define glib2_version 2.79.2
|
||||
|
||||
Loading…
Reference in New Issue
Block a user