forked from paperwm/PaperWM
-
Notifications
You must be signed in to change notification settings - Fork 0
/
extension.js
216 lines (175 loc) · 6.52 KB
/
extension.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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
// polyfill workspace_manager that was introduced in 3.30 (must happen before modules are imported)
if (!global.workspace_manager) {
global.workspace_manager = global.screen;
}
/**
The currently used modules
- tiling is the main module, responsible for tiling and workspaces
- navigator is used to initiate a discrete navigation.
Focus is only switched when the navigation is done.
- keybindings is a utility wrapper around mutters keybinding facilities.
- scratch is used to manage floating windows, or scratch windows.
- liveAltTab is a simple altTab implementiation with live previews.
- stackoverlay is somewhat kludgy. It makes clicking on the left or right
edge of the screen always activate the partially (or sometimes wholly)
concealed window at the edges.
- app creates new windows based on the current application. It's possible
to create custom new window handlers.
- kludges is used for monkey patching gnome shell behavior which simply
doesn't fit paperwm.
- topbar adds the workspace name to the topbar and styles it.
- gestures is responsible for 3-finger swiping (only works in wayland).
*/
var modules = [
'tiling', 'navigator', 'keybindings', 'scratch', 'liveAltTab', 'utils',
'stackoverlay', 'app', 'kludges', 'topbar', 'settings','gestures'
];
/**
Tell the modules to run init, enable or disable
*/
function run(method) {
for (let name of modules) {
// Bail if there's an error in our own modules
if (!safeCall(name, method))
return false;
}
if (hasUserConfigFile()) {
safeCall('user', method);
}
return true;
}
function safeCall(name, method) {
try {
log("#paperwm", `${method} ${name}`);
let module = Extension.imports[name];
module && module[method] && module[method].call(module, errorNotification);
return true;
} catch(e) {
log("#paperwm", `${name} failed ${method}`);
log(`JS ERROR: ${e}\n${e.stack}`);
errorNotification(
"PaperWM",
`Error occured in ${name} @${method}:\n\n${e.message}`,
e.stack);
return false;
}
}
var SESSIONID = ""+(new Date().getTime());
/**
* The extension sometimes go through multiple init -> enable -> disable
* cycles. So we need to keep track of whether we're initialized..
*/
var initRun;
var enabled = false;
var Extension, convenience;
function init() {
SESSIONID += "#";
log(`#paperwm init: ${SESSIONID}`);
// var Gio = imports.gi.Gio;
// let extfile = Gio.file_new_for_path( Extension.imports.extension.__file__);
Extension = imports.misc.extensionUtils.getCurrentExtension();
convenience = Extension.imports.convenience;
if(initRun) {
log(`#startup Reinitialized against our will! Skip adding bindings again to not cause trouble.`);
return;
}
initUserConfig();
if (run('init'))
initRun = true;
}
function enable() {
log(`#paperwm enable ${SESSIONID}`);
if (enabled) {
log('enable called without calling disable');
return;
}
if (run('enable'))
enabled = true;
}
function disable() {
log(`#paperwm disable ${SESSIONID}`);
if (!enabled) {
log('disable called without calling enable');
return;
}
if (run('disable'))
enabled = false;
}
var Gio = imports.gi.Gio;
var GLib = imports.gi.GLib;
var Main = imports.ui.main;
function getConfigDir() {
return Gio.file_new_for_path(GLib.get_user_config_dir() + '/paperwm');
}
function hasUserConfigFile() {
return getConfigDir().get_child("user.js").query_exists(null);
}
function installConfig() {
log("#rc", "Installing config");
const configDir = getConfigDir();
configDir.make_directory_with_parents(null);
// We copy metadata.json to the config directory so gnome-shell-mode
// know which extension the files belong to (ideally we'd symlink, but
// that trips up the importer: Extension.imports.<complete> in
// gnome-shell-mode crashes gnome-shell..)
const metadata = Extension.dir.get_child("metadata.json");
metadata.copy(configDir.get_child("metadata.json"), Gio.FileCopyFlags.NONE, null, null);
// Copy the user.js template to the config directory
const user = Extension.dir.get_child("examples/user.js");
user.copy(configDir.get_child("user.js"), Gio.FileCopyFlags.NONE, null, null);
const settings = convenience.getSettings();
settings.set_boolean("has-installed-config-template", true);
}
function initUserConfig() {
const paperSettings = convenience.getSettings();
if (!paperSettings.get_boolean("has-installed-config-template")
&& !hasUserConfigFile())
{
try {
installConfig();
const configDir = getConfigDir().get_path();
const notification = notify("PaperWM", `Installed user configuration in ${configDir}`);
notification.connect('activated', () => {
imports.misc.util.spawn(["nautilus", configDir]);
notification.destroy();
});
} catch(e) {
errorNotification("PaperWM",
`Failed to install user config: ${e.message}`, e.stack);
log("#rc", "Install failed", e.message);
}
}
if (hasUserConfigFile()) {
Extension.imports.searchPath.push(getConfigDir().get_path());
}
}
/**
* Our own version of imports.ui.main.notify allowing more control over the
* notification
*/
function notify(msg, details, params) {
const MessageTray = imports.ui.messageTray;
let source = new MessageTray.SystemNotificationSource();
// note-to-self: the source is automatically destroyed when all its
// notifications are removed.
Main.messageTray.add(source);
let notification = new MessageTray.Notification(source, msg, details, params);
notification.setResident(true); // Usually more annoying that the notification disappear than not
source.notify(notification);
return notification;
}
function spawnPager(content) {
const quoted = GLib.shell_quote(content);
imports.misc.util.spawn(["sh", "-c", `echo -En ${quoted} | gedit --new-window -`]);
}
/**
* Show an notification opening a the full message in dedicated window upon
* activation
*/
function errorNotification(title, message, fullMessage) {
const notification = notify(title, message);
notification.connect('activated', () => {
spawnPager([title, message, "", fullMessage].join("\n"));
notification.destroy();
});
}