2014-02-14 12:35:05 +00:00
|
|
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
import Gio from 'gi://Gio';
|
|
|
|
import GLib from 'gi://GLib';
|
|
|
|
import IBus from 'gi://IBus';
|
|
|
|
import Meta from 'gi://Meta';
|
|
|
|
import Shell from 'gi://Shell';
|
2014-02-14 12:35:05 +00:00
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
import * as Signals from './signals.js';
|
|
|
|
import * as BoxPointer from '../ui/boxpointer.js';
|
|
|
|
|
|
|
|
import * as IBusCandidatePopup from '../ui/ibusCandidatePopup.js';
|
2018-01-17 16:01:24 +00:00
|
|
|
|
2019-12-19 19:50:37 +00:00
|
|
|
Gio._promisify(IBus.Bus.prototype,
|
|
|
|
'list_engines_async', 'list_engines_async_finish');
|
|
|
|
Gio._promisify(IBus.Bus.prototype,
|
|
|
|
'request_name_async', 'request_name_async_finish');
|
|
|
|
Gio._promisify(IBus.Bus.prototype,
|
|
|
|
'get_global_engine_async', 'get_global_engine_async_finish');
|
|
|
|
Gio._promisify(IBus.Bus.prototype,
|
|
|
|
'set_global_engine_async', 'set_global_engine_async_finish');
|
2022-02-10 13:08:06 +00:00
|
|
|
Gio._promisify(Shell, 'util_systemd_unit_exists');
|
2019-12-19 19:50:37 +00:00
|
|
|
|
2018-01-17 16:01:24 +00:00
|
|
|
// Ensure runtime version matches
|
|
|
|
_checkIBusVersion(1, 5, 2);
|
2014-02-14 12:35:05 +00:00
|
|
|
|
|
|
|
let _ibusManager = null;
|
2022-02-10 13:08:06 +00:00
|
|
|
const IBUS_SYSTEMD_SERVICE = 'org.freedesktop.IBus.session.GNOME.service';
|
2014-02-14 12:35:05 +00:00
|
|
|
|
2022-04-22 11:28:02 +00:00
|
|
|
const TYPING_BOOSTER_ENGINE = 'typing-booster';
|
|
|
|
const IBUS_TYPING_BOOSTER_SCHEMA = 'org.freedesktop.ibus.engine.typing-booster';
|
|
|
|
const KEY_EMOJIPREDICTIONS = 'emojipredictions';
|
|
|
|
const KEY_DICTIONARY = 'dictionary';
|
|
|
|
const KEY_INLINECOMPLETION = 'inlinecompletion';
|
|
|
|
const KEY_INPUTMETHOD = 'inputmethod';
|
|
|
|
|
2014-11-27 08:23:01 +00:00
|
|
|
function _checkIBusVersion(requiredMajor, requiredMinor, requiredMicro) {
|
2014-11-10 10:09:08 +00:00
|
|
|
if ((IBus.MAJOR_VERSION > requiredMajor) ||
|
|
|
|
(IBus.MAJOR_VERSION == requiredMajor && IBus.MINOR_VERSION > requiredMinor) ||
|
|
|
|
(IBus.MAJOR_VERSION == requiredMajor && IBus.MINOR_VERSION == requiredMinor &&
|
|
|
|
IBus.MICRO_VERSION >= requiredMicro))
|
|
|
|
return;
|
|
|
|
|
2022-02-07 14:14:06 +00:00
|
|
|
throw new Error(`Found IBus version ${
|
|
|
|
IBus.MAJOR_VERSION}.${IBus.MINOR_VERSION}.${IBus.MINOR_VERSION} ` +
|
|
|
|
`but required is ${requiredMajor}.${requiredMinor}.${requiredMicro}`);
|
2014-11-10 10:09:08 +00:00
|
|
|
}
|
|
|
|
|
2023-07-30 12:56:59 +00:00
|
|
|
/**
|
|
|
|
* @returns {IBusManager}
|
|
|
|
*/
|
2023-07-10 09:53:00 +00:00
|
|
|
export function getIBusManager() {
|
2014-02-14 12:35:05 +00:00
|
|
|
if (_ibusManager == null)
|
|
|
|
_ibusManager = new IBusManager();
|
|
|
|
return _ibusManager;
|
|
|
|
}
|
|
|
|
|
2023-07-10 09:53:00 +00:00
|
|
|
class IBusManager extends Signals.EventEmitter {
|
2017-10-31 01:19:44 +00:00
|
|
|
constructor() {
|
2022-07-04 22:30:44 +00:00
|
|
|
super();
|
|
|
|
|
2017-10-31 01:19:44 +00:00
|
|
|
IBus.init();
|
2014-02-14 12:35:05 +00:00
|
|
|
|
2017-10-31 01:19:44 +00:00
|
|
|
// This is the longest we'll keep the keyboard frozen until an input
|
|
|
|
// source is active.
|
|
|
|
this._MAX_INPUT_SOURCE_ACTIVATION_TIME = 4000; // ms
|
|
|
|
this._PRELOAD_ENGINES_DELAY_TIME = 30; // sec
|
2014-06-05 16:47:48 +00:00
|
|
|
|
2014-02-14 12:35:05 +00:00
|
|
|
|
|
|
|
this._candidatePopup = new IBusCandidatePopup.CandidatePopup();
|
|
|
|
|
|
|
|
this._panelService = null;
|
2019-06-29 23:02:34 +00:00
|
|
|
this._engines = new Map();
|
2014-02-14 12:35:05 +00:00
|
|
|
this._ready = false;
|
|
|
|
this._registerPropertiesId = 0;
|
|
|
|
this._currentEngineName = null;
|
2014-11-10 10:09:08 +00:00
|
|
|
this._preloadEnginesId = 0;
|
2014-02-14 12:35:05 +00:00
|
|
|
|
|
|
|
this._ibus = IBus.Bus.new_async();
|
2017-12-02 00:27:35 +00:00
|
|
|
this._ibus.connect('connected', this._onConnected.bind(this));
|
|
|
|
this._ibus.connect('disconnected', this._clear.bind(this));
|
2014-02-14 12:35:05 +00:00
|
|
|
// Need to set this to get 'global-engine-changed' emitions
|
|
|
|
this._ibus.set_watch_ibus_signal(true);
|
2017-12-02 00:27:35 +00:00
|
|
|
this._ibus.connect('global-engine-changed', this._engineChanged.bind(this));
|
2014-05-29 13:27:42 +00:00
|
|
|
|
2022-02-10 13:08:06 +00:00
|
|
|
this._queueSpawn();
|
|
|
|
}
|
|
|
|
|
|
|
|
async _ibusSystemdServiceExists() {
|
|
|
|
if (this._ibusIsSystemdService)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
try {
|
|
|
|
this._ibusIsSystemdService =
|
|
|
|
await Shell.util_systemd_unit_exists(
|
|
|
|
IBUS_SYSTEMD_SERVICE, null);
|
|
|
|
} catch (e) {
|
|
|
|
this._ibusIsSystemdService = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return this._ibusIsSystemdService;
|
|
|
|
}
|
|
|
|
|
|
|
|
async _queueSpawn() {
|
|
|
|
const isSystemdService = await this._ibusSystemdServiceExists();
|
|
|
|
if (!isSystemdService)
|
|
|
|
this._spawn(Meta.is_wayland_compositor() ? [] : ['--xim']);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-05-29 13:27:42 +00:00
|
|
|
|
2022-06-27 00:56:48 +00:00
|
|
|
_tryAppendEnv(env, varname) {
|
|
|
|
const value = GLib.getenv(varname);
|
|
|
|
if (value)
|
|
|
|
env.push(`${varname}=${value}`);
|
|
|
|
}
|
|
|
|
|
2019-08-15 11:28:59 +00:00
|
|
|
_spawn(extraArgs = []) {
|
2014-05-29 13:27:42 +00:00
|
|
|
try {
|
2019-08-15 11:29:53 +00:00
|
|
|
let cmdLine = ['ibus-daemon', '--panel', 'disable', ...extraArgs];
|
2022-01-31 09:40:04 +00:00
|
|
|
let env = [];
|
|
|
|
|
2022-06-27 00:56:48 +00:00
|
|
|
this._tryAppendEnv(env, 'DBUS_SESSION_BUS_ADDRESS');
|
|
|
|
this._tryAppendEnv(env, 'WAYLAND_DISPLAY');
|
|
|
|
this._tryAppendEnv(env, 'HOME');
|
|
|
|
this._tryAppendEnv(env, 'LANG');
|
|
|
|
this._tryAppendEnv(env, 'LC_CTYPE');
|
|
|
|
this._tryAppendEnv(env, 'COMPOSE_FILE');
|
|
|
|
this._tryAppendEnv(env, 'DISPLAY');
|
|
|
|
|
2022-06-27 20:38:55 +00:00
|
|
|
// Use DO_NOT_REAP_CHILD to avoid adouble-fork internally
|
|
|
|
// since ibus-daemon refuses to start with init as its parent.
|
|
|
|
const [success_, pid] = GLib.spawn_async(
|
2022-01-31 09:40:04 +00:00
|
|
|
null, cmdLine, env,
|
2022-06-27 20:38:55 +00:00
|
|
|
GLib.SpawnFlags.SEARCH_PATH | GLib.SpawnFlags.DO_NOT_REAP_CHILD,
|
2022-01-31 09:40:04 +00:00
|
|
|
() => {
|
|
|
|
try {
|
|
|
|
global.context.restore_rlimit_nofile();
|
|
|
|
} catch (err) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
2022-06-27 20:38:55 +00:00
|
|
|
GLib.child_watch_add(
|
|
|
|
GLib.PRIORITY_DEFAULT,
|
|
|
|
pid,
|
|
|
|
() => GLib.spawn_close_pid(pid)
|
|
|
|
);
|
2019-01-29 01:26:39 +00:00
|
|
|
} catch (e) {
|
2019-01-30 00:18:24 +00:00
|
|
|
log(`Failed to launch ibus-daemon: ${e.message}`);
|
2014-05-29 13:27:42 +00:00
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-02-14 12:35:05 +00:00
|
|
|
|
2022-02-10 13:08:06 +00:00
|
|
|
async restartDaemon(extraArgs = []) {
|
|
|
|
const isSystemdService = await this._ibusSystemdServiceExists();
|
|
|
|
if (!isSystemdService)
|
|
|
|
this._spawn(['-r', ...extraArgs]);
|
2019-08-15 11:28:59 +00:00
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_clear() {
|
ibusManager, inputMethod: Cancel async ibus calls chain on disconnect
The shell tries to spawn the ibus daemon on startup if unavailable, however
as per commit 8adfc5b1 we also force restarting it once the X11 server is
available.
Unfortunately this could cause a race if we disconnect while we were already
connected to an ibus daemon, but still in the process of going through the
various nested calls.
In fact the ::disconnect callback didn't stop any further async ibus call
that, even if failing, would have eventually triggered the emission of a
'ready' signal and to the Keyboard's callback, leading under X11 to a full
grab owned by ibus daemon.
In order to avoid this and keep control of the calls order, use in both
IbusManager and InputMethod a cancellable that is setup before connecting to
the bus, and that is cancelled on disconnection.
Then handle the finish() calls properly, using try/catch to validate the
returned value, taking in account the potential error and just not
proceeding in case of cancellation.
Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1712
2019-09-25 18:56:46 +00:00
|
|
|
if (this._cancellable) {
|
|
|
|
this._cancellable.cancel();
|
|
|
|
this._cancellable = null;
|
|
|
|
}
|
|
|
|
|
2019-09-25 20:15:38 +00:00
|
|
|
if (this._preloadEnginesId) {
|
|
|
|
GLib.source_remove(this._preloadEnginesId);
|
|
|
|
this._preloadEnginesId = 0;
|
|
|
|
}
|
|
|
|
|
2014-02-14 12:35:05 +00:00
|
|
|
if (this._panelService)
|
|
|
|
this._panelService.destroy();
|
|
|
|
|
|
|
|
this._panelService = null;
|
|
|
|
this._candidatePopup.setPanelService(null);
|
2019-06-29 23:02:34 +00:00
|
|
|
this._engines.clear();
|
2014-02-14 12:35:05 +00:00
|
|
|
this._ready = false;
|
|
|
|
this._registerPropertiesId = 0;
|
|
|
|
this._currentEngineName = null;
|
|
|
|
|
|
|
|
this.emit('ready', false);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-02-14 12:35:05 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_onConnected() {
|
ibusManager, inputMethod: Cancel async ibus calls chain on disconnect
The shell tries to spawn the ibus daemon on startup if unavailable, however
as per commit 8adfc5b1 we also force restarting it once the X11 server is
available.
Unfortunately this could cause a race if we disconnect while we were already
connected to an ibus daemon, but still in the process of going through the
various nested calls.
In fact the ::disconnect callback didn't stop any further async ibus call
that, even if failing, would have eventually triggered the emission of a
'ready' signal and to the Keyboard's callback, leading under X11 to a full
grab owned by ibus daemon.
In order to avoid this and keep control of the calls order, use in both
IbusManager and InputMethod a cancellable that is setup before connecting to
the bus, and that is cancelled on disconnection.
Then handle the finish() calls properly, using try/catch to validate the
returned value, taking in account the potential error and just not
proceeding in case of cancellation.
Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1712
2019-09-25 18:56:46 +00:00
|
|
|
this._cancellable = new Gio.Cancellable();
|
2019-12-19 19:50:37 +00:00
|
|
|
this._initEngines();
|
|
|
|
this._initPanelService();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-02-14 12:35:05 +00:00
|
|
|
|
2019-12-19 19:50:37 +00:00
|
|
|
async _initEngines() {
|
ibusManager, inputMethod: Cancel async ibus calls chain on disconnect
The shell tries to spawn the ibus daemon on startup if unavailable, however
as per commit 8adfc5b1 we also force restarting it once the X11 server is
available.
Unfortunately this could cause a race if we disconnect while we were already
connected to an ibus daemon, but still in the process of going through the
various nested calls.
In fact the ::disconnect callback didn't stop any further async ibus call
that, even if failing, would have eventually triggered the emission of a
'ready' signal and to the Keyboard's callback, leading under X11 to a full
grab owned by ibus daemon.
In order to avoid this and keep control of the calls order, use in both
IbusManager and InputMethod a cancellable that is setup before connecting to
the bus, and that is cancelled on disconnection.
Then handle the finish() calls properly, using try/catch to validate the
returned value, taking in account the potential error and just not
proceeding in case of cancellation.
Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1712
2019-09-25 18:56:46 +00:00
|
|
|
try {
|
2019-12-19 19:50:37 +00:00
|
|
|
const enginesList =
|
|
|
|
await this._ibus.list_engines_async(-1, this._cancellable);
|
2014-02-14 12:35:05 +00:00
|
|
|
for (let i = 0; i < enginesList.length; ++i) {
|
|
|
|
let name = enginesList[i].get_name();
|
2019-06-29 23:02:34 +00:00
|
|
|
this._engines.set(name, enginesList[i]);
|
2014-02-14 12:35:05 +00:00
|
|
|
}
|
|
|
|
this._updateReadiness();
|
ibusManager, inputMethod: Cancel async ibus calls chain on disconnect
The shell tries to spawn the ibus daemon on startup if unavailable, however
as per commit 8adfc5b1 we also force restarting it once the X11 server is
available.
Unfortunately this could cause a race if we disconnect while we were already
connected to an ibus daemon, but still in the process of going through the
various nested calls.
In fact the ::disconnect callback didn't stop any further async ibus call
that, even if failing, would have eventually triggered the emission of a
'ready' signal and to the Keyboard's callback, leading under X11 to a full
grab owned by ibus daemon.
In order to avoid this and keep control of the calls order, use in both
IbusManager and InputMethod a cancellable that is setup before connecting to
the bus, and that is cancelled on disconnection.
Then handle the finish() calls properly, using try/catch to validate the
returned value, taking in account the potential error and just not
proceeding in case of cancellation.
Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1712
2019-09-25 18:56:46 +00:00
|
|
|
} catch (e) {
|
|
|
|
if (e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
|
|
|
return;
|
|
|
|
|
|
|
|
logError(e);
|
2014-02-14 12:35:05 +00:00
|
|
|
this._clear();
|
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-02-14 12:35:05 +00:00
|
|
|
|
2019-12-19 19:50:37 +00:00
|
|
|
async _initPanelService() {
|
ibusManager, inputMethod: Cancel async ibus calls chain on disconnect
The shell tries to spawn the ibus daemon on startup if unavailable, however
as per commit 8adfc5b1 we also force restarting it once the X11 server is
available.
Unfortunately this could cause a race if we disconnect while we were already
connected to an ibus daemon, but still in the process of going through the
various nested calls.
In fact the ::disconnect callback didn't stop any further async ibus call
that, even if failing, would have eventually triggered the emission of a
'ready' signal and to the Keyboard's callback, leading under X11 to a full
grab owned by ibus daemon.
In order to avoid this and keep control of the calls order, use in both
IbusManager and InputMethod a cancellable that is setup before connecting to
the bus, and that is cancelled on disconnection.
Then handle the finish() calls properly, using try/catch to validate the
returned value, taking in account the potential error and just not
proceeding in case of cancellation.
Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1712
2019-09-25 18:56:46 +00:00
|
|
|
try {
|
2019-12-19 19:50:37 +00:00
|
|
|
await this._ibus.request_name_async(IBus.SERVICE_PANEL,
|
|
|
|
IBus.BusNameFlag.REPLACE_EXISTING, -1, this._cancellable);
|
ibusManager, inputMethod: Cancel async ibus calls chain on disconnect
The shell tries to spawn the ibus daemon on startup if unavailable, however
as per commit 8adfc5b1 we also force restarting it once the X11 server is
available.
Unfortunately this could cause a race if we disconnect while we were already
connected to an ibus daemon, but still in the process of going through the
various nested calls.
In fact the ::disconnect callback didn't stop any further async ibus call
that, even if failing, would have eventually triggered the emission of a
'ready' signal and to the Keyboard's callback, leading under X11 to a full
grab owned by ibus daemon.
In order to avoid this and keep control of the calls order, use in both
IbusManager and InputMethod a cancellable that is setup before connecting to
the bus, and that is cancelled on disconnection.
Then handle the finish() calls properly, using try/catch to validate the
returned value, taking in account the potential error and just not
proceeding in case of cancellation.
Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1712
2019-09-25 18:56:46 +00:00
|
|
|
} catch (e) {
|
2020-03-23 22:22:17 +00:00
|
|
|
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
|
|
|
|
logError(e);
|
|
|
|
this._clear();
|
|
|
|
}
|
|
|
|
return;
|
ibusManager, inputMethod: Cancel async ibus calls chain on disconnect
The shell tries to spawn the ibus daemon on startup if unavailable, however
as per commit 8adfc5b1 we also force restarting it once the X11 server is
available.
Unfortunately this could cause a race if we disconnect while we were already
connected to an ibus daemon, but still in the process of going through the
various nested calls.
In fact the ::disconnect callback didn't stop any further async ibus call
that, even if failing, would have eventually triggered the emission of a
'ready' signal and to the Keyboard's callback, leading under X11 to a full
grab owned by ibus daemon.
In order to avoid this and keep control of the calls order, use in both
IbusManager and InputMethod a cancellable that is setup before connecting to
the bus, and that is cancelled on disconnection.
Then handle the finish() calls properly, using try/catch to validate the
returned value, taking in account the potential error and just not
proceeding in case of cancellation.
Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1712
2019-09-25 18:56:46 +00:00
|
|
|
}
|
|
|
|
|
2020-03-23 22:22:17 +00:00
|
|
|
this._panelService = new IBus.PanelService({
|
|
|
|
connection: this._ibus.get_connection(),
|
|
|
|
object_path: IBus.PATH_PANEL,
|
|
|
|
});
|
|
|
|
this._candidatePopup.setPanelService(this._panelService);
|
|
|
|
this._panelService.connect('update-property', this._updateProperty.bind(this));
|
|
|
|
this._panelService.connect('set-cursor-location', (ps, x, y, w, h) => {
|
|
|
|
let cursorLocation = { x, y, width: w, height: h };
|
|
|
|
this.emit('set-cursor-location', cursorLocation);
|
|
|
|
});
|
|
|
|
this._panelService.connect('focus-in', (panel, path) => {
|
|
|
|
if (!GLib.str_has_suffix(path, '/InputContext_1'))
|
|
|
|
this.emit('focus-in');
|
|
|
|
});
|
|
|
|
this._panelService.connect('focus-out', () => this.emit('focus-out'));
|
2018-05-25 09:35:49 +00:00
|
|
|
|
2020-03-23 22:22:17 +00:00
|
|
|
try {
|
|
|
|
// IBus versions older than 1.5.10 have a bug which
|
|
|
|
// causes spurious set-content-type emissions when
|
|
|
|
// switching input focus that temporarily lose purpose
|
|
|
|
// and hints defeating its intended semantics and
|
|
|
|
// confusing users. We thus don't use it in that case.
|
|
|
|
_checkIBusVersion(1, 5, 10);
|
|
|
|
this._panelService.connect('set-content-type', this._setContentType.bind(this));
|
|
|
|
} catch (e) {
|
|
|
|
}
|
2020-05-03 15:26:39 +00:00
|
|
|
this._updateReadiness();
|
2019-12-19 19:50:37 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
// If an engine is already active we need to get its properties
|
|
|
|
const engine =
|
|
|
|
await this._ibus.get_global_engine_async(-1, this._cancellable);
|
2020-03-23 22:22:17 +00:00
|
|
|
this._engineChanged(this._ibus, engine.get_name());
|
2019-12-19 19:50:37 +00:00
|
|
|
} catch (e) {
|
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-02-14 12:35:05 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_updateReadiness() {
|
2019-06-29 23:02:34 +00:00
|
|
|
this._ready = this._engines.size > 0 && this._panelService != null;
|
2014-02-14 12:35:05 +00:00
|
|
|
this.emit('ready', this._ready);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-02-14 12:35:05 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_engineChanged(bus, engineName) {
|
2014-02-14 12:35:05 +00:00
|
|
|
if (!this._ready)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._currentEngineName = engineName;
|
2023-05-24 08:34:42 +00:00
|
|
|
this._candidatePopup.close(BoxPointer.PopupAnimation.NONE);
|
2014-02-14 12:35:05 +00:00
|
|
|
|
|
|
|
if (this._registerPropertiesId != 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._registerPropertiesId =
|
2017-10-31 00:38:18 +00:00
|
|
|
this._panelService.connect('register-properties', (p, props) => {
|
2014-02-14 12:35:05 +00:00
|
|
|
if (!props.get(0))
|
|
|
|
return;
|
|
|
|
|
|
|
|
this._panelService.disconnect(this._registerPropertiesId);
|
|
|
|
this._registerPropertiesId = 0;
|
|
|
|
|
|
|
|
this.emit('properties-registered', this._currentEngineName, props);
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-02-14 12:35:05 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_updateProperty(panel, prop) {
|
2014-02-14 12:35:05 +00:00
|
|
|
this.emit('property-updated', this._currentEngineName, prop);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-02-14 12:35:05 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_setContentType(panel, purpose, hints) {
|
2014-11-27 08:23:01 +00:00
|
|
|
this.emit('set-content-type', purpose, hints);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-11-27 08:23:01 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
activateProperty(key, state) {
|
2014-02-14 12:35:05 +00:00
|
|
|
this._panelService.property_activate(key, state);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-02-14 12:35:05 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
getEngineDesc(id) {
|
2019-06-29 23:02:34 +00:00
|
|
|
if (!this._ready || !this._engines.has(id))
|
2014-02-14 12:35:05 +00:00
|
|
|
return null;
|
|
|
|
|
2019-06-29 23:02:34 +00:00
|
|
|
return this._engines.get(id);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-06-05 16:47:48 +00:00
|
|
|
|
2022-10-17 13:55:11 +00:00
|
|
|
async _setEngine(id, callback) {
|
2014-11-27 08:23:01 +00:00
|
|
|
// Send id even if id == this._currentEngineName
|
|
|
|
// because 'properties-registered' signal can be emitted
|
|
|
|
// while this._ibusSources == null on a lock screen.
|
2018-01-17 16:01:24 +00:00
|
|
|
if (!this._ready) {
|
2014-06-05 16:47:48 +00:00
|
|
|
if (callback)
|
|
|
|
callback();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-12-19 19:50:37 +00:00
|
|
|
try {
|
|
|
|
await this._ibus.set_global_engine_async(id,
|
|
|
|
this._MAX_INPUT_SOURCE_ACTIVATION_TIME,
|
|
|
|
this._cancellable);
|
|
|
|
} catch (e) {
|
|
|
|
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
|
|
|
|
logError(e);
|
|
|
|
}
|
2022-10-17 13:55:11 +00:00
|
|
|
|
2019-12-19 19:50:37 +00:00
|
|
|
if (callback)
|
|
|
|
callback();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-11-10 10:09:08 +00:00
|
|
|
|
2022-10-17 13:55:11 +00:00
|
|
|
async setEngine(id, callback) {
|
|
|
|
if (this._preOskState)
|
|
|
|
this._preOskState.engine = id;
|
|
|
|
|
|
|
|
const isXkb = id.startsWith('xkb:');
|
|
|
|
if (this._oskCompletion && isXkb)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (this._oskCompletion)
|
|
|
|
this.setCompletionEnabled(false, callback);
|
|
|
|
else
|
|
|
|
await this._setEngine(id, callback);
|
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
preloadEngines(ids) {
|
2022-04-22 11:28:02 +00:00
|
|
|
if (!this._ibus || !this._ready)
|
2014-11-10 10:09:08 +00:00
|
|
|
return;
|
|
|
|
|
2022-04-22 11:28:02 +00:00
|
|
|
if (!ids.includes(TYPING_BOOSTER_ENGINE))
|
|
|
|
ids.push(TYPING_BOOSTER_ENGINE);
|
|
|
|
|
2014-11-10 10:09:08 +00:00
|
|
|
if (this._preloadEnginesId != 0) {
|
2019-08-19 18:50:33 +00:00
|
|
|
GLib.source_remove(this._preloadEnginesId);
|
2014-11-10 10:09:08 +00:00
|
|
|
this._preloadEnginesId = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._preloadEnginesId =
|
2019-08-19 18:50:33 +00:00
|
|
|
GLib.timeout_add_seconds(
|
|
|
|
GLib.PRIORITY_DEFAULT,
|
|
|
|
this._PRELOAD_ENGINES_DELAY_TIME,
|
|
|
|
() => {
|
|
|
|
this._ibus.preload_engines_async(
|
|
|
|
ids,
|
|
|
|
-1,
|
ibusManager, inputMethod: Cancel async ibus calls chain on disconnect
The shell tries to spawn the ibus daemon on startup if unavailable, however
as per commit 8adfc5b1 we also force restarting it once the X11 server is
available.
Unfortunately this could cause a race if we disconnect while we were already
connected to an ibus daemon, but still in the process of going through the
various nested calls.
In fact the ::disconnect callback didn't stop any further async ibus call
that, even if failing, would have eventually triggered the emission of a
'ready' signal and to the Keyboard's callback, leading under X11 to a full
grab owned by ibus daemon.
In order to avoid this and keep control of the calls order, use in both
IbusManager and InputMethod a cancellable that is setup before connecting to
the bus, and that is cancelled on disconnection.
Then handle the finish() calls properly, using try/catch to validate the
returned value, taking in account the potential error and just not
proceeding in case of cancellation.
Fixes https://gitlab.gnome.org/GNOME/gnome-shell/issues/1712
2019-09-25 18:56:46 +00:00
|
|
|
this._cancellable,
|
2019-08-19 18:50:33 +00:00
|
|
|
null);
|
|
|
|
this._preloadEnginesId = 0;
|
|
|
|
return GLib.SOURCE_REMOVE;
|
|
|
|
});
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2022-04-22 11:28:02 +00:00
|
|
|
|
2022-10-17 11:29:10 +00:00
|
|
|
setCompletionEnabled(enabled, callback) {
|
2022-04-22 11:28:02 +00:00
|
|
|
/* Needs typing-booster available */
|
2022-10-17 13:55:11 +00:00
|
|
|
if (enabled && !this._engines.has(TYPING_BOOSTER_ENGINE))
|
2022-04-22 11:28:02 +00:00
|
|
|
return false;
|
|
|
|
/* Can do only on xkb engines */
|
|
|
|
if (enabled && !this._currentEngineName.startsWith('xkb:'))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (this._oskCompletion === enabled)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
this._oskCompletion = enabled;
|
|
|
|
let settings =
|
|
|
|
new Gio.Settings({schema_id: IBUS_TYPING_BOOSTER_SCHEMA});
|
|
|
|
|
|
|
|
if (enabled) {
|
|
|
|
this._preOskState = {
|
|
|
|
'engine': this._currentEngineName,
|
|
|
|
'emoji': settings.get_value(KEY_EMOJIPREDICTIONS),
|
|
|
|
'langs': settings.get_value(KEY_DICTIONARY),
|
|
|
|
'completion': settings.get_value(KEY_INLINECOMPLETION),
|
|
|
|
'inputMethod': settings.get_value(KEY_INPUTMETHOD),
|
|
|
|
};
|
|
|
|
settings.reset(KEY_EMOJIPREDICTIONS);
|
|
|
|
|
|
|
|
const removeEncoding = l => l.replace(/\..*/, '');
|
|
|
|
const removeDups = (l, pos, arr) => {
|
|
|
|
return !pos || arr[pos - 1] !== l;
|
|
|
|
};
|
|
|
|
settings.set_string(
|
|
|
|
KEY_DICTIONARY,
|
|
|
|
GLib.get_language_names().map(removeEncoding)
|
|
|
|
.sort().filter(removeDups).join(','));
|
|
|
|
|
|
|
|
settings.reset(KEY_INLINECOMPLETION);
|
|
|
|
settings.set_string(KEY_INPUTMETHOD, 'NoIME');
|
2022-10-17 13:55:11 +00:00
|
|
|
this._setEngine(TYPING_BOOSTER_ENGINE, callback);
|
2022-04-22 11:28:02 +00:00
|
|
|
} else if (this._preOskState) {
|
|
|
|
const {engine, emoji, langs, completion, inputMethod} =
|
|
|
|
this._preOskState;
|
|
|
|
this._preOskState = null;
|
2022-10-17 13:55:11 +00:00
|
|
|
this._setEngine(engine, callback);
|
2022-04-22 11:28:02 +00:00
|
|
|
settings.set_value(KEY_EMOJIPREDICTIONS, emoji);
|
|
|
|
settings.set_value(KEY_DICTIONARY, langs);
|
|
|
|
settings.set_value(KEY_INLINECOMPLETION, completion);
|
|
|
|
settings.set_value(KEY_INPUTMETHOD, inputMethod);
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
2023-07-10 09:53:00 +00:00
|
|
|
}
|