util: add Util.spawn and friends
Add Util.spawn, Util.spawnCommandLine, and Util.spawnDesktop for spawning a command/argv/.desktop file in the background, automatically handling errors via MessageTray.SystemNotificationSource(), and Util.trySpawn, Util.trySpawnCommandLine, and Utils.trySpawnDesktop that don't do automatic error handling (but do at least clean up the error message in the exception a bit). Update various other bits of code around the shell to use the new methods. https://bugzilla.gnome.org/show_bug.cgi?id=635089
This commit is contained in:
parent
a65a0f03d4
commit
8bdfb8df68
131
js/misc/util.js
131
js/misc/util.js
@ -1,5 +1,16 @@
|
||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
||||
|
||||
const Gdk = imports.gi.Gdk;
|
||||
const Gio = imports.gi.Gio;
|
||||
const GLib = imports.gi.GLib;
|
||||
const Shell = imports.gi.Shell;
|
||||
|
||||
const Main = imports.ui.main;
|
||||
const MessageTray = imports.ui.messageTray;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
|
||||
/* http://daringfireball.net/2010/07/improved_regex_for_matching_urls */
|
||||
const _urlRegexp = new RegExp('\b(([a-z][\w-]+:(/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)([^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:\'".,<>?«»“”‘’]))', 'gi');
|
||||
|
||||
@ -17,3 +28,123 @@ function findUrls(str) {
|
||||
res.push({ url: match[0], pos: match.index });
|
||||
return res;
|
||||
}
|
||||
|
||||
// spawn:
|
||||
// @argv: an argv array
|
||||
//
|
||||
// Runs @argv in the background, handling any errors that occur
|
||||
// when trying to start the program.
|
||||
function spawn(argv) {
|
||||
try {
|
||||
trySpawn(argv);
|
||||
} catch (err) {
|
||||
_handleSpawnError(argv[0], err);
|
||||
}
|
||||
}
|
||||
|
||||
// spawnCommandLine:
|
||||
// @command_line: a command line
|
||||
//
|
||||
// Runs @command_line in the background, handling any errors that
|
||||
// occur when trying to parse or start the program.
|
||||
function spawnCommandLine(command_line) {
|
||||
try {
|
||||
let [success, argc, argv] = GLib.shell_parse_argv(command_line);
|
||||
trySpawn(argv);
|
||||
} catch (err) {
|
||||
_handleSpawnError(command_line, err);
|
||||
}
|
||||
}
|
||||
|
||||
// spawnDesktop:
|
||||
// @id: a desktop file ID
|
||||
//
|
||||
// Spawns the desktop file identified by @id using startup notification,
|
||||
// etc, handling any errors that occur when trying to find or start
|
||||
// the program.
|
||||
function spawnDesktop(id) {
|
||||
try {
|
||||
trySpawnDesktop(id);
|
||||
} catch (err) {
|
||||
_handleSpawnError(id, err);
|
||||
}
|
||||
}
|
||||
|
||||
// trySpawn:
|
||||
// @argv: an argv array
|
||||
//
|
||||
// Runs @argv in the background. If launching @argv fails,
|
||||
// this will throw an error.
|
||||
function trySpawn(argv)
|
||||
{
|
||||
try {
|
||||
GLib.spawn_async(null, argv, null,
|
||||
GLib.SpawnFlags.SEARCH_PATH,
|
||||
null, null);
|
||||
} catch (err) {
|
||||
// The exception from gjs contains an error string like:
|
||||
// Error invoking GLib.spawn_command_line_async: Failed to
|
||||
// execute child process "foo" (No such file or directory)
|
||||
// We are only interested in the part in the parentheses. (And
|
||||
// we can't pattern match the text, since it gets localized.)
|
||||
err.message = err.message.replace(/.*\((.+)\)/, '$1');
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// trySpawnCommandLine:
|
||||
// @command_line: a command line
|
||||
//
|
||||
// Runs @command_line in the background. If launching @command_line
|
||||
// fails, this will throw an error.
|
||||
function trySpawnCommandLine(command_line) {
|
||||
let success, argc, argv;
|
||||
|
||||
try {
|
||||
[success, argc, argv] = GLib.shell_parse_argv(command_line);
|
||||
} catch (err) {
|
||||
// Replace "Error invoking GLib.shell_parse_argv: " with
|
||||
// something nicer
|
||||
err.message = err.message.replace(/[^:]*: /, _("Could not parse command:") + "\n");
|
||||
throw err;
|
||||
}
|
||||
|
||||
trySpawn(argv);
|
||||
}
|
||||
|
||||
// trySpawnDesktop:
|
||||
// @id: a desktop file ID
|
||||
//
|
||||
// Spawns the desktop file identified by @id using startup notification.
|
||||
// On error, throws an exception.
|
||||
function trySpawnDesktop(id) {
|
||||
let app;
|
||||
|
||||
// shell_app_system_load_from_desktop_file() will end up returning
|
||||
// a stupid error message if the desktop file doesn't exist, but
|
||||
// that's the only case it returns an error for, so we just
|
||||
// substitute our own error in instead
|
||||
try {
|
||||
app = Shell.AppSystem.get_default().load_from_desktop_file(id + '.desktop');
|
||||
} catch (err) {
|
||||
throw new Error(_("No such application"));
|
||||
}
|
||||
|
||||
try {
|
||||
app.launch();
|
||||
} catch(err) {
|
||||
// see trySpawn
|
||||
err.message = err.message.replace(/.*\((.+)\)/, '$1');
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
function _handleSpawnError(command, err) {
|
||||
let title = _("Execution of '%s' failed:").format(command);
|
||||
|
||||
let source = new MessageTray.SystemNotificationSource();
|
||||
Main.messageTray.add(source);
|
||||
let notification = new MessageTray.Notification(source, title, err.message);
|
||||
notification.setTransient(true);
|
||||
source.notify(notification);
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ const WindowManager = imports.ui.windowManager;
|
||||
const Magnifier = imports.ui.magnifier;
|
||||
const XdndHandler = imports.ui.xdndHandler;
|
||||
const StatusIconDispatcher = imports.ui.statusIconDispatcher;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
|
||||
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
|
||||
@ -317,11 +318,8 @@ function _globalKeyPressHandler(actor, event) {
|
||||
if (action == Meta.KeyBindingAction.COMMAND_SCREENSHOT) {
|
||||
let gconf = GConf.Client.get_default();
|
||||
let command = gconf.get_string('/apps/metacity/keybinding_commands/command_screenshot');
|
||||
if (command != null && command != '') {
|
||||
let [ok, len, args] = GLib.shell_parse_argv(command);
|
||||
let p = new Shell.Process({'args' : args});
|
||||
p.run();
|
||||
}
|
||||
if (command != null && command != '')
|
||||
Util.spawnCommandLine(command);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -106,8 +106,7 @@ URLHighlighter.prototype = {
|
||||
return true;
|
||||
} catch (e) {
|
||||
// TODO: remove this after gnome 3 release
|
||||
let p = new Shell.Process({ 'args' : ['gvfs-open', url] });
|
||||
p.run();
|
||||
Util.spawn(['gvfs-open', url]);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ const _ = Gettext.gettext;
|
||||
const DND = imports.ui.dnd;
|
||||
const Main = imports.ui.main;
|
||||
const Search = imports.ui.search;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const NAUTILUS_PREFS_DIR = '/apps/nautilus/preferences';
|
||||
const DESKTOP_IS_HOME_KEY = NAUTILUS_PREFS_DIR + '/desktop_is_home_dir';
|
||||
@ -163,7 +164,7 @@ PlacesManager.prototype = {
|
||||
icon_size: size });
|
||||
},
|
||||
function () {
|
||||
new Shell.Process({ args: ['nautilus-connect-server'] }).run();
|
||||
Util.spawn(['nautilus-connect-server']);
|
||||
});
|
||||
|
||||
let networkApp = null;
|
||||
|
@ -14,6 +14,7 @@ const _ = Gettext.gettext;
|
||||
const Lightbox = imports.ui.lightbox;
|
||||
const Main = imports.ui.main;
|
||||
const Tweener = imports.ui.tweener;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const MAX_FILE_DELETED_BEFORE_INVALID = 10;
|
||||
|
||||
@ -354,9 +355,7 @@ RunDialog.prototype = {
|
||||
try {
|
||||
if (inTerminal)
|
||||
command = 'gnome-terminal -x ' + input;
|
||||
let [ok, len, args] = GLib.shell_parse_argv(command);
|
||||
let p = new Shell.Process({ 'args' : args });
|
||||
p.run();
|
||||
Util.trySpawnCommandLine(command);
|
||||
} catch (e) {
|
||||
// Mmmh, that failed - see if @input matches an existing file
|
||||
let path = null;
|
||||
@ -374,13 +373,8 @@ RunDialog.prototype = {
|
||||
global.create_app_launch_context());
|
||||
} else {
|
||||
this._commandError = true;
|
||||
// The exception contains an error string like:
|
||||
// Error invoking Shell.run: Failed to execute child
|
||||
// process "foo" (No such file or directory)
|
||||
// We are only interested in the actual error, so parse
|
||||
//that out.
|
||||
let m = /.+\((.+)\)/.exec(e);
|
||||
let errorStr = _("Execution of '%s' failed:").format(command) + '\n' + m[1];
|
||||
|
||||
let errorStr = _("Execution of '%s' failed:").format(command) + '\n' + e.message;
|
||||
this._errorMessage.set_text(errorStr);
|
||||
|
||||
this._errorBox.show();
|
||||
|
@ -12,6 +12,7 @@ const St = imports.gi.St;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
@ -108,8 +109,7 @@ ATIndicator.prototype = {
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addAction(_("Universal Access Settings"), function() {
|
||||
let p = new Shell.Process({ args: ['gnome-control-center','universal-access'] });
|
||||
p.run();
|
||||
Util.spawnDesktop('gnome-universal-access-panel');
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -10,6 +10,7 @@ const St = imports.gi.St;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
@ -83,7 +84,7 @@ Indicator.prototype = {
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
|
||||
this.menu.addAction(_("Power Settings"),function() {
|
||||
GLib.spawn_command_line_async('gnome-control-center power');
|
||||
Util.spawnDesktop('gnome-power-panel');
|
||||
});
|
||||
|
||||
this._proxy.connect('Changed', Lang.bind(this, this._devicesChanged));
|
||||
@ -134,8 +135,7 @@ Indicator.prototype = {
|
||||
this._batteryItem.actor.reactive = true;
|
||||
this._batteryItem.actor.can_focus = true;
|
||||
this._batteryItem.connect('activate', function(item) {
|
||||
let p = new Shell.Process({ args: ['gnome-power-statistics', '--device', device_id] });
|
||||
p.run();
|
||||
Util.spawn(['gnome-power-statistics', '--device', device_id]);
|
||||
});
|
||||
} else {
|
||||
// virtual device
|
||||
@ -164,8 +164,7 @@ Indicator.prototype = {
|
||||
|
||||
let item = new DeviceItem (devices[i]);
|
||||
item.connect('activate', function() {
|
||||
let p = new Shell.Process({ args: ['gnome-power-statistics', '--device', device_id] });
|
||||
p.run();
|
||||
Util.spawn(['gnome-power-statistics', '--device', device_id]);
|
||||
});
|
||||
this._deviceItems.push(item);
|
||||
this.menu.addMenuItem(item, this._otherDevicePosition + position);
|
||||
@ -198,7 +197,7 @@ Indicator.prototype = {
|
||||
_checkError: function(error) {
|
||||
if (!this._restarted && error && error.message.match(/org\.freedesktop\.DBus\.Error\.(UnknownMethod|InvalidArgs)/)) {
|
||||
GLib.spawn_command_line_sync('pkill -f "^gnome-power-manager$"');
|
||||
GLib.spawn_command_line_async('gnome-power-manager');
|
||||
Util.spawn(['gnome-power-manager']);
|
||||
this._restarted = true;
|
||||
}
|
||||
}
|
||||
|
@ -11,6 +11,7 @@ const St = imports.gi.St;
|
||||
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
const Gettext = imports.gettext.domain('gnome-shell');
|
||||
const _ = Gettext.gettext;
|
||||
@ -60,8 +61,7 @@ Indicator.prototype = {
|
||||
|
||||
this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
|
||||
this.menu.addAction(_("Sound Settings"), function() {
|
||||
let p = new Shell.Process({ args: ['gnome-control-center', 'sound'] });
|
||||
p.run();
|
||||
Util.spawnDesktop('gnome-sound-panel');
|
||||
});
|
||||
|
||||
this.actor.connect('scroll-event', Lang.bind(this, this._onScrollEvent));
|
||||
|
@ -12,6 +12,7 @@ const GnomeSession = imports.misc.gnomeSession;
|
||||
const Main = imports.ui.main;
|
||||
const PanelMenu = imports.ui.panelMenu;
|
||||
const PopupMenu = imports.ui.popupMenu;
|
||||
const Util = imports.misc.util;
|
||||
|
||||
// Adapted from gdm/gui/user-switch-applet/applet.c
|
||||
//
|
||||
@ -31,7 +32,7 @@ StatusMenuButton.prototype = {
|
||||
this.actor.set_child(box);
|
||||
|
||||
this._gdm = Gdm.UserManager.ref_default();
|
||||
this._gdm.queue_load()
|
||||
this._gdm.queue_load();
|
||||
|
||||
this._user = this._gdm.get_user(GLib.get_user_name());
|
||||
this._presence = new GnomeSession.Presence();
|
||||
@ -153,17 +154,17 @@ StatusMenuButton.prototype = {
|
||||
|
||||
_onMyAccountActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._spawn(['gnome-control-center', 'user-accounts']);
|
||||
Util.spawnDesktop('gnome-user-accounts-panel');
|
||||
},
|
||||
|
||||
_onPreferencesActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._spawn(['gnome-control-center', '-o']);
|
||||
Util.spawnDesktop('gnome-control-center');
|
||||
},
|
||||
|
||||
_onLockScreenActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._spawn(['gnome-screensaver-command', '--lock']);
|
||||
Util.spawn(['gnome-screensaver-command', '--lock']);
|
||||
},
|
||||
|
||||
_onLoginScreenActivate: function() {
|
||||
@ -174,19 +175,11 @@ StatusMenuButton.prototype = {
|
||||
|
||||
_onQuitSessionActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._spawn(['gnome-session-save', '--logout-dialog']);
|
||||
Util.spawn(['gnome-session-save', '--logout-dialog']);
|
||||
},
|
||||
|
||||
_onShutDownActivate: function() {
|
||||
Main.overview.hide();
|
||||
this._spawn(['gnome-session-save', '--shutdown-dialog']);
|
||||
},
|
||||
|
||||
_spawn: function(args) {
|
||||
// FIXME: once Shell.Process gets support for signalling
|
||||
// errors we should pop up an error dialog or something here
|
||||
// on failure
|
||||
let p = new Shell.Process({'args' : args});
|
||||
p.run();
|
||||
Util.spawn(['gnome-session-save', '--shutdown-dialog']);
|
||||
}
|
||||
};
|
||||
|
@ -1,6 +1,7 @@
|
||||
data/gnome-shell.desktop.in.in
|
||||
data/org.gnome.shell.gschema.xml.in
|
||||
data/org.gnome.accessibility.magnifier.gschema.xml.in
|
||||
js/misc/util.js
|
||||
js/ui/appDisplay.js
|
||||
js/ui/appFavorites.js
|
||||
js/ui/dash.js
|
||||
|
Loading…
Reference in New Issue
Block a user