2014-02-14 12:35:05 +00:00
|
|
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
2019-01-31 14:07:06 +00:00
|
|
|
/* exported getIBusManager */
|
2014-02-14 12:35:05 +00:00
|
|
|
|
2019-02-09 03:21:36 +00:00
|
|
|
const { Gio, GLib, IBus } = imports.gi;
|
2014-11-10 10:09:08 +00:00
|
|
|
const Mainloop = imports.mainloop;
|
2014-02-14 12:35:05 +00:00
|
|
|
const Signals = imports.signals;
|
|
|
|
|
2018-01-17 16:01:24 +00:00
|
|
|
const IBusCandidatePopup = imports.ui.ibusCandidatePopup;
|
|
|
|
|
|
|
|
// Ensure runtime version matches
|
|
|
|
_checkIBusVersion(1, 5, 2);
|
2014-02-14 12:35:05 +00:00
|
|
|
|
|
|
|
let _ibusManager = null;
|
|
|
|
|
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;
|
|
|
|
|
2019-08-19 19:12:13 +00:00
|
|
|
throw "Found IBus version %d.%d.%d but required is %d.%d.%d"
|
|
|
|
.format(IBus.MAJOR_VERSION, IBus.MINOR_VERSION, IBus.MINOR_VERSION,
|
|
|
|
requiredMajor, requiredMinor, requiredMicro);
|
2014-11-10 10:09:08 +00:00
|
|
|
}
|
|
|
|
|
2014-02-14 12:35:05 +00:00
|
|
|
function getIBusManager() {
|
|
|
|
if (_ibusManager == null)
|
|
|
|
_ibusManager = new IBusManager();
|
|
|
|
return _ibusManager;
|
|
|
|
}
|
|
|
|
|
2017-10-31 01:19:44 +00:00
|
|
|
var IBusManager = class {
|
|
|
|
constructor() {
|
|
|
|
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
|
|
|
|
|
|
|
this._spawn();
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-05-29 13:27:42 +00:00
|
|
|
|
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];
|
2019-08-15 11:28:59 +00:00
|
|
|
Gio.Subprocess.new(cmdLine, Gio.SubprocessFlags.NONE);
|
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
|
|
|
|
2019-08-15 11:28:59 +00:00
|
|
|
restartDaemon(extraArgs = []) {
|
|
|
|
this._spawn(['-r', ...extraArgs]);
|
|
|
|
}
|
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_clear() {
|
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() {
|
2017-12-02 00:27:35 +00:00
|
|
|
this._ibus.list_engines_async(-1, null, this._initEngines.bind(this));
|
2014-02-14 12:35:05 +00:00
|
|
|
this._ibus.request_name_async(IBus.SERVICE_PANEL,
|
|
|
|
IBus.BusNameFlag.REPLACE_EXISTING,
|
|
|
|
-1, null,
|
2017-12-02 00:27:35 +00:00
|
|
|
this._initPanelService.bind(this));
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-02-14 12:35:05 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_initEngines(ibus, result) {
|
2014-02-14 12:35:05 +00:00
|
|
|
let enginesList = this._ibus.list_engines_async_finish(result);
|
|
|
|
if (enginesList) {
|
|
|
|
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();
|
|
|
|
} else {
|
|
|
|
this._clear();
|
|
|
|
}
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-02-14 12:35:05 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
_initPanelService(ibus, result) {
|
2014-02-14 12:35:05 +00:00
|
|
|
let success = this._ibus.request_name_async_finish(result);
|
|
|
|
if (success) {
|
|
|
|
this._panelService = new IBus.PanelService({ connection: this._ibus.get_connection(),
|
|
|
|
object_path: IBus.PATH_PANEL });
|
|
|
|
this._candidatePopup.setPanelService(this._panelService);
|
2017-12-02 00:27:35 +00:00
|
|
|
this._panelService.connect('update-property', this._updateProperty.bind(this));
|
2018-05-25 09:35:49 +00:00
|
|
|
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);
|
|
|
|
});
|
2018-09-18 10:54:29 +00:00
|
|
|
this._panelService.connect('focus-in', (panel, path) => {
|
|
|
|
if (!GLib.str_has_suffix(path, '/InputContext_1'))
|
|
|
|
this.emit ('focus-in');
|
|
|
|
});
|
2019-01-28 00:42:00 +00:00
|
|
|
this._panelService.connect('focus-out', () => this.emit('focus-out'));
|
2018-05-25 09:35:49 +00:00
|
|
|
|
2014-11-27 08:23:01 +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);
|
2017-12-02 00:27:35 +00:00
|
|
|
this._panelService.connect('set-content-type', this._setContentType.bind(this));
|
2014-11-27 08:23:01 +00:00
|
|
|
} catch (e) {
|
|
|
|
}
|
2014-02-14 12:35:05 +00:00
|
|
|
// If an engine is already active we need to get its properties
|
2017-10-31 00:38:18 +00:00
|
|
|
this._ibus.get_global_engine_async(-1, null, (i, result) => {
|
2014-02-14 12:35:05 +00:00
|
|
|
let engine;
|
|
|
|
try {
|
|
|
|
engine = this._ibus.get_global_engine_async_finish(result);
|
|
|
|
if (!engine)
|
|
|
|
return;
|
2019-01-29 01:26:39 +00:00
|
|
|
} catch (e) {
|
2014-02-14 12:35:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
this._engineChanged(this._ibus, engine.get_name());
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2014-02-14 12:35:05 +00:00
|
|
|
this._updateReadiness();
|
|
|
|
} else {
|
|
|
|
this._clear();
|
|
|
|
}
|
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;
|
|
|
|
|
|
|
|
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
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._ibus.set_global_engine_async(id, this._MAX_INPUT_SOURCE_ACTIVATION_TIME,
|
2018-11-15 17:47:55 +00:00
|
|
|
null, callback || null);
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
2014-11-10 10:09:08 +00:00
|
|
|
|
2017-10-31 00:03:21 +00:00
|
|
|
preloadEngines(ids) {
|
2018-01-17 16:01:24 +00:00
|
|
|
if (!this._ibus || ids.length == 0)
|
2014-11-10 10:09:08 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
if (this._preloadEnginesId != 0) {
|
|
|
|
Mainloop.source_remove(this._preloadEnginesId);
|
|
|
|
this._preloadEnginesId = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
this._preloadEnginesId =
|
|
|
|
Mainloop.timeout_add_seconds(this._PRELOAD_ENGINES_DELAY_TIME,
|
2017-10-31 00:38:18 +00:00
|
|
|
() => {
|
2014-11-10 10:09:08 +00:00
|
|
|
this._ibus.preload_engines_async(
|
|
|
|
ids,
|
|
|
|
-1,
|
|
|
|
null,
|
|
|
|
null);
|
|
|
|
this._preloadEnginesId = 0;
|
|
|
|
return GLib.SOURCE_REMOVE;
|
2017-10-31 00:38:18 +00:00
|
|
|
});
|
2017-10-31 01:19:44 +00:00
|
|
|
}
|
|
|
|
};
|
2014-02-14 12:35:05 +00:00
|
|
|
Signals.addSignalMethods(IBusManager.prototype);
|