-
Notifications
You must be signed in to change notification settings - Fork 3
/
util.js
134 lines (121 loc) · 4.79 KB
/
util.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/**
* Workspaces Switcher Manager
* utils.js
*
* @author GdH <G-dH@github.com>
* @copyright 2022-2024
* @license GPL-3.0
*/
import GLib from 'gi://GLib';
import Gio from 'gi://Gio';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import { InjectionManager } from 'resource:///org/gnome/shell/extensions/extension.js';
let _installedExtensions;
export class Overrides extends InjectionManager {
constructor() {
super();
this._overrides = {};
}
addOverride(name, prototype, overrideList) {
const backup = this.overrideProto(prototype, overrideList, name);
// don't update originals when override's just refreshing, keep initial content
let originals = this._overrides[name]?.originals;
if (!originals)
originals = backup;
this._overrides[name] = {
originals,
prototype,
};
}
removeOverride(name) {
const override = this._overrides[name];
if (!override)
return false;
this.overrideProto(override.prototype, override.originals, name);
delete this._overrides[name];
return true;
}
removeAll() {
for (let name in this._overrides) {
this.removeOverride(name);
delete this._overrides[name];
}
}
overrideProto(proto, overrides, name) {
const backup = {};
const originals = this._overrides[name]?.originals;
for (let symbol in overrides) {
if (symbol.startsWith('after_')) {
const actualSymbol = symbol.slice('after_'.length);
let fn;
if (originals && originals[actualSymbol])
fn = originals[actualSymbol];
else
fn = proto[actualSymbol];
const afterFn = overrides[symbol];
proto[actualSymbol] = function (...args) {
args = Array.prototype.slice.call(args);
const res = fn.apply(this, args);
afterFn.apply(this, args);
return res;
};
backup[actualSymbol] = fn;
} else if (overrides[symbol] !== null) {
backup[symbol] = proto[symbol];
this._installMethod(proto, symbol, overrides[symbol]);
}
}
return backup;
}
}
export function getEnabledExtensions(pattern = '') {
let result = [];
// extensionManager is unreliable at startup because it is uncertain whether all extensions have been loaded
// also gsettings key can contain already removed extensions (user deleted them without disabling them first)
// therefore we have to check what's really installed in the filesystem
if (!_installedExtensions) {
const extensionFiles = [...collectFromDatadirs('extensions', true)];
_installedExtensions = extensionFiles.map(({ info }) => {
let fileType = info.get_file_type();
if (fileType !== Gio.FileType.DIRECTORY)
return null;
const uuid = info.get_name();
return uuid;
});
}
// _enabledExtensions contains content of the enabled-extensions key from gsettings, not actual state
const enabled = Main.extensionManager._enabledExtensions;
result = _installedExtensions.filter(ext => enabled.includes(ext));
// _extensions contains already loaded extensions, so we can try to filter out broken or incompatible extensions
const active = Main.extensionManager._extensions;
result = result.filter(ext => {
const extension = active.get(ext);
if (extension)
return ![3, 4].includes(extension.state); // 3 - ERROR, 4 - OUT_OF_TIME (not supported by shell-version in metadata)
// extension can be enabled but not yet loaded, we just cannot see its state at this moment, so let it pass as enabled
return true;
});
// return only extensions matching the search pattern
return result.filter(uuid => uuid !== null && uuid.includes(pattern));
}
function* collectFromDatadirs(subdir, includeUserDir) {
let dataDirs = GLib.get_system_data_dirs();
if (includeUserDir)
dataDirs.unshift(GLib.get_user_data_dir());
for (let i = 0; i < dataDirs.length; i++) {
let path = GLib.build_filenamev([dataDirs[i], 'gnome-shell', subdir]);
let dir = Gio.File.new_for_path(path);
let fileEnum;
try {
fileEnum = dir.enumerate_children('standard::name,standard::type',
Gio.FileQueryInfoFlags.NONE, null);
} catch (e) {
fileEnum = null;
}
if (fileEnum !== null) {
let info;
while ((info = fileEnum.next_file(null)))
yield { dir: fileEnum.get_child(info), info };
}
}
}