dotfiles/.local/share/gnome-shell/extensions/gsconnect@andyholmes.github.io/service/components/clipboard.js

284 lines
7.6 KiB
JavaScript

'use strict';
const Gdk = imports.gi.Gdk;
const GLib = imports.gi.GLib;
const Gtk = imports.gi.Gtk;
const Gio = imports.gi.Gio;
const GObject = imports.gi.GObject;
const DBUS_NAME = 'org.gnome.Shell.Extensions.GSConnect.Clipboard';
const DBUS_PATH = '/org/gnome/Shell/Extensions/GSConnect/Clipboard';
var Clipboard = GObject.registerClass({
GTypeName: 'GSConnectClipboard',
Properties: {
'text': GObject.ParamSpec.string(
'text',
'Text Content',
'The current text content of the clipboard',
GObject.ParamFlags.READWRITE,
''
),
},
}, class Clipboard extends GObject.Object {
_init() {
super._init();
this._cancellable = new Gio.Cancellable();
this._clipboard = null;
this._ownerChangeId = 0;
this._nameWatcherId = Gio.bus_watch_name(
Gio.BusType.SESSION,
DBUS_NAME,
Gio.BusNameWatcherFlags.NONE,
this._onNameAppeared.bind(this),
this._onNameVanished.bind(this)
);
}
get text() {
if (this._text === undefined)
this._text = '';
return this._text;
}
set text(content) {
if (this.text === content)
return;
this._text = content;
this.notify('text');
if (typeof content !== 'string')
return;
if (this._clipboard instanceof Gtk.Clipboard)
this._clipboard.set_text(content, -1);
if (this._clipboard instanceof Gio.DBusProxy)
this._proxySetText(content);
}
async _onNameAppeared(connection, name, name_owner) {
try {
// Cleanup the GtkClipboard
if (this._clipboard && this._ownerChangeId > 0) {
this._clipboard.disconnect(this._ownerChangeId);
this._ownerChangeId = 0;
}
// Create a proxy for the remote clipboard
this._clipboard = new Gio.DBusProxy({
g_bus_type: Gio.BusType.SESSION,
g_name: DBUS_NAME,
g_object_path: DBUS_PATH,
g_interface_name: DBUS_NAME,
g_flags: Gio.DBusProxyFlags.DO_NOT_LOAD_PROPERTIES,
});
await new Promise((resolve, reject) => {
this._clipboard.init_async(
GLib.PRIORITY_DEFAULT,
this._cancellable,
(proxy, res) => {
try {
resolve(proxy.init_finish(res));
} catch (e) {
reject(e);
}
}
);
});
this._ownerChangeId = this._clipboard.connect(
'g-signal',
this._onOwnerChange.bind(this)
);
this._onOwnerChange();
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) {
debug(e);
this._onNameVanished(null, null);
}
}
}
_onNameVanished(connection, name) {
if (this._clipboard && this._ownerChangeId > 0) {
this._clipboard.disconnect(this._ownerChangeId);
this._clipboardChangedId = 0;
}
const display = Gdk.Display.get_default();
this._clipboard = Gtk.Clipboard.get_default(display);
this._ownerChangeId = this._clipboard.connect(
'owner-change',
this._onOwnerChange.bind(this)
);
this._onOwnerChange();
}
async _onOwnerChange() {
try {
if (this._clipboard instanceof Gtk.Clipboard)
await this._gtkUpdateText();
else if (this._clipboard instanceof Gio.DBusProxy)
await this._proxyUpdateText();
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
debug(e);
}
}
_applyUpdate(text) {
if (typeof text !== 'string' || this.text === text)
return;
this._text = text;
this.notify('text');
}
/*
* Proxy Clipboard
*/
_proxyGetMimetypes() {
return new Promise((resolve, reject) => {
this._clipboard.call(
'GetMimetypes',
null,
Gio.DBusCallFlags.NO_AUTO_START,
-1,
this._cancellable,
(proxy, res) => {
try {
const reply = proxy.call_finish(res);
resolve(reply.deepUnpack()[0]);
} catch (e) {
Gio.DBusError.strip_remote_error(e);
reject(e);
}
}
);
});
}
_proxyGetText() {
return new Promise((resolve, reject) => {
this._clipboard.call(
'GetText',
null,
Gio.DBusCallFlags.NO_AUTO_START,
-1,
this._cancellable,
(proxy, res) => {
try {
const reply = proxy.call_finish(res);
resolve(reply.deepUnpack()[0]);
} catch (e) {
Gio.DBusError.strip_remote_error(e);
reject(e);
}
}
);
});
}
_proxySetText(text) {
this._clipboard.call(
'SetText',
new GLib.Variant('(s)', [text]),
Gio.DBusCallFlags.NO_AUTO_START,
-1,
this._cancellable,
(proxy, res) => {
try {
proxy.call_finish(res);
} catch (e) {
Gio.DBusError.strip_remote_error(e);
debug(e);
}
}
);
}
async _proxyUpdateText() {
const mimetypes = await this._proxyGetMimetypes();
// Special case for a cleared clipboard
if (mimetypes.length === 0)
return this._applyUpdate('');
// Special case to ignore copied files
if (mimetypes.includes('text/uri-list'))
return;
const text = await this._proxyGetText();
this._applyUpdate(text);
}
/*
* GtkClipboard
*/
_gtkGetMimetypes() {
return new Promise((resolve, reject) => {
this._clipboard.request_targets((clipboard, atoms) => resolve(atoms));
});
}
_gtkGetText() {
return new Promise((resolve, reject) => {
this._clipboard.request_text((clipboard, text) => resolve(text));
});
}
async _gtkUpdateText() {
const mimetypes = await this._gtkGetMimetypes();
// Special case for a cleared clipboard
if (mimetypes.length === 0)
return this._applyUpdate('');
// Special case to ignore copied files
if (mimetypes.includes('text/uri-list'))
return;
const text = await this._gtkGetText();
this._applyUpdate(text);
}
destroy() {
if (this._cancellable.is_cancelled())
return;
this._cancellable.cancel();
if (this._clipboard && this._ownerChangeId > 0) {
this._clipboard.disconnect(this._ownerChangeId);
this._ownerChangedId = 0;
}
if (this._nameWatcherId > 0) {
Gio.bus_unwatch_name(this._nameWatcherId);
this._nameWatcherId = 0;
}
}
});
/**
* The service class for this component
*/
var Component = Clipboard;