-
Notifications
You must be signed in to change notification settings - Fork 130
/
app.js
186 lines (158 loc) · 6.1 KB
/
app.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
import GLib from 'gi://GLib';
import Gio from 'gi://Gio';
import Shell from 'gi://Shell';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import { Patches, Tiling } from './imports.js';
/*
Application functionality, like global new window actions etc.
*/
let Tracker = Shell.WindowTracker.get_default();
let CouldNotLaunch = Symbol();
// Lookup table for custom handlers, keys being the app id
export let customHandlers, customSpawnHandlers;
export function enable() {
customHandlers = { 'org.gnome.Terminal.desktop': newGnomeTerminal };
customSpawnHandlers = {
'com.gexperts.Tilix.desktop': mkCommandLineSpawner('tilix --working-directory %d'),
};
function spawnWithFallback(fallback, ...args) {
try {
return trySpawnWindow(...args);
} catch (e) {
return fallback();
}
}
let overrideWithFallback = Patches.overrideWithFallback;
overrideWithFallback(
Shell.App, "open_new_window",
(fallback, app, workspaceId) => {
return spawnWithFallback(fallback, app, global.workspace_manager.get_workspace_by_index(workspaceId));
}
);
overrideWithFallback(
Shell.App, "launch_action",
(fallback, app, name, ...args) => {
if (name === 'new-window')
return spawnWithFallback(fallback, app);
else {
return fallback();
}
}
);
overrideWithFallback(
Gio.DesktopAppInfo, "launch",
(fallback, appInfo) => {
return spawnWithFallback(fallback, appInfo.get_id());
}
);
overrideWithFallback(
Gio.DesktopAppInfo, "launch_action",
(fallback, appInfo, name, ...args) => {
if (name === 'new-window')
return spawnWithFallback(fallback, appInfo.get_id());
else {
return fallback();
}
}
);
}
export function disable() {
customHandlers = null;
customSpawnHandlers = null;
}
export function launchFromWorkspaceDir(app, workspace = null) {
if (typeof app === 'string') {
app = new Shell.App({ app_info: Gio.DesktopAppInfo.new(app) });
}
let dir = getWorkspaceDirectory(workspace);
let cmd = app.app_info.get_commandline();
if (!cmd || dir == '') {
throw CouldNotLaunch;
}
/* Note: One would think working directory could be specified in the AppLaunchContext
The dbus spec https://specifications.freedesktop.org/desktop-entry-spec/1.1/ar01s07.html
indicates otherwise (for dbus activated actions). Can affect arbitrary environment
variables of exec activated actions, but no environment variable determine working
directory of new processes. */
// TODO: substitute correct values according to https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#exec-variables
cmd = cmd.replace(/%./g, "");
let [success, cmdArgs] = GLib.shell_parse_argv(cmd);
if (!success) {
console.error("launchFromWorkspaceDir:", "Could not parse command line", cmd);
throw CouldNotLaunch;
}
GLib.spawn_async(dir, cmdArgs, GLib.get_environ(), GLib.SpawnFlags.SEARCH_PATH, null);
}
export function newGnomeTerminal(metaWindow, app) {
/* Note: this action activation is _not_ bound to the window - instead it
relies on the window being active when called.
If the new window doesn't start in the same directory it's probably
because 'vte.sh' haven't been sourced by the shell in this terminal */
app.action_group.activate_action(
"win.new-terminal", new GLib.Variant("(ss)", ["window", "current"]));
}
export function duplicateWindow(metaWindow) {
metaWindow = metaWindow || global.display.focus_window;
let app = Tracker.get_window_app(metaWindow);
let handler = customHandlers[app.id];
if (handler) {
let space = Tiling.spaces.spaceOfWindow(metaWindow);
return handler(metaWindow, app, space);
}
let workspaceId = metaWindow.get_workspace().workspace_index;
let original = Patches.getSavedProp(Shell.App.prototype, "open_new_window");
original.call(app, workspaceId);
return true;
}
export function trySpawnWindow(app, workspace) {
if (typeof app === 'string') {
app = new Shell.App({ app_info: Gio.DesktopAppInfo.new(app) });
}
let handler = customSpawnHandlers[app.id];
if (handler) {
let space = Tiling.spaces.selectedSpace;
return handler(app, space);
} else {
launchFromWorkspaceDir(app, workspace);
}
}
export function spawnWindow(app, workspace) {
if (typeof app === 'string') {
app = new Shell.App({ app_info: Gio.DesktopAppInfo.new(app) });
}
try {
return trySpawnWindow(app, workspace);
} catch (e) {
// Let the overide take care any fallback
return app.open_new_window(-1);
}
}
export function getWorkspaceDirectory(workspace = null) {
let space = workspace ? Tiling.spaces.get(workspace) : Tiling.spaces.selectedSpace;
let dir = space.settings.get_string("directory");
if (dir[0] === "~") {
dir = GLib.getenv("HOME") + dir.slice(1);
}
return dir;
}
export function expandCommandline(commandline, workspace) {
let dir = getWorkspaceDirectory(workspace);
commandline = commandline.replace(/%d/g, () => GLib.shell_quote(dir));
return commandline;
}
export function mkCommandLineSpawner(commandlineTemplate, spawnInWorkspaceDir = false) {
return (app, space) => {
let workspace = space.workspace;
let commandline = expandCommandline(commandlineTemplate, workspace);
let workingDir = spawnInWorkspaceDir ? getWorkspaceDirectory(workspace) : null;
let [success, cmdArgs] = GLib.shell_parse_argv(commandline);
if (success) {
success = GLib.spawn_async(workingDir, cmdArgs, GLib.get_environ(), GLib.SpawnFlags.SEARCH_PATH, null);
}
if (!success) {
Main.notifyError(
`Failed to run custom spawn handler for ${app.id}`,
`Attempted to run '${commandline}'`);
}
};
}