2008-12-01 14:51:43 -05:00
|
|
|
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
|
2008-10-31 14:09:20 -04:00
|
|
|
|
2010-05-20 16:42:40 -04:00
|
|
|
imports.gi.versions.Clutter = '1.0';
|
|
|
|
imports.gi.versions.Gio = '2.0';
|
2010-06-19 14:26:16 -04:00
|
|
|
imports.gi.versions.Gdk = '3.0';
|
2010-05-20 16:42:40 -04:00
|
|
|
imports.gi.versions.GdkPixbuf = '2.0';
|
2010-06-19 14:26:16 -04:00
|
|
|
imports.gi.versions.Gtk = '3.0';
|
2010-05-20 16:42:40 -04:00
|
|
|
|
2008-10-31 00:22:44 -04:00
|
|
|
const Clutter = imports.gi.Clutter;
|
2009-09-23 14:30:05 -04:00
|
|
|
const DBus = imports.dbus;
|
2009-05-01 14:13:51 -04:00
|
|
|
const Gdk = imports.gi.Gdk;
|
2009-01-22 16:28:19 -05:00
|
|
|
const Gio = imports.gi.Gio;
|
2009-08-28 15:11:25 -04:00
|
|
|
const GLib = imports.gi.GLib;
|
2010-05-05 17:05:42 -04:00
|
|
|
const GConf = imports.gi.GConf;
|
2009-05-07 09:47:48 -04:00
|
|
|
const Lang = imports.lang;
|
2008-12-05 16:50:09 -05:00
|
|
|
const Mainloop = imports.mainloop;
|
2009-04-29 14:01:09 -04:00
|
|
|
const Meta = imports.gi.Meta;
|
2009-02-02 18:02:16 -05:00
|
|
|
const Shell = imports.gi.Shell;
|
2009-09-10 01:36:41 -04:00
|
|
|
const St = imports.gi.St;
|
2010-07-01 14:13:42 -04:00
|
|
|
const Gettext = imports.gettext.domain('gnome-shell');
|
|
|
|
const _ = Gettext.gettext;
|
2008-10-31 00:22:44 -04:00
|
|
|
|
2009-05-06 14:36:50 -04:00
|
|
|
const Chrome = imports.ui.chrome;
|
2010-07-01 14:13:42 -04:00
|
|
|
const CtrlAltTab = imports.ui.ctrlAltTab;
|
2011-01-06 10:30:15 -05:00
|
|
|
const EndSessionDialog = imports.ui.endSessionDialog;
|
2011-02-21 12:17:34 -05:00
|
|
|
const PolkitAuthenticationAgent = imports.ui.polkitAuthenticationAgent;
|
2009-09-18 15:51:15 -04:00
|
|
|
const Environment = imports.ui.environment;
|
2009-10-25 18:53:10 -04:00
|
|
|
const ExtensionSystem = imports.ui.extensionSystem;
|
2010-01-13 15:05:20 -05:00
|
|
|
const MessageTray = imports.ui.messageTray;
|
2009-08-11 07:46:10 -04:00
|
|
|
const Overview = imports.ui.overview;
|
2009-02-02 18:02:16 -05:00
|
|
|
const Panel = imports.ui.panel;
|
2009-10-31 22:25:28 -04:00
|
|
|
const PlaceDisplay = imports.ui.placeDisplay;
|
2008-12-09 17:10:43 -05:00
|
|
|
const RunDialog = imports.ui.runDialog;
|
2009-08-02 03:46:01 -04:00
|
|
|
const LookingGlass = imports.ui.lookingGlass;
|
2010-01-13 15:05:20 -05:00
|
|
|
const NotificationDaemon = imports.ui.notificationDaemon;
|
2010-02-22 13:53:41 -05:00
|
|
|
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
|
2010-05-09 13:42:35 -04:00
|
|
|
const Scripting = imports.ui.scripting;
|
2009-09-23 14:30:05 -04:00
|
|
|
const ShellDBus = imports.ui.shellDBus;
|
2010-02-02 10:21:47 -05:00
|
|
|
const TelepathyClient = imports.ui.telepathyClient;
|
2008-12-09 17:10:43 -05:00
|
|
|
const WindowManager = imports.ui.windowManager;
|
2010-05-10 12:40:03 -04:00
|
|
|
const Magnifier = imports.ui.magnifier;
|
2011-01-05 09:47:27 -05:00
|
|
|
const XdndHandler = imports.ui.xdndHandler;
|
2010-08-09 12:33:34 -04:00
|
|
|
const StatusIconDispatcher = imports.ui.statusIconDispatcher;
|
2010-11-17 11:43:08 -05:00
|
|
|
const Util = imports.misc.util;
|
2008-10-31 14:09:20 -04:00
|
|
|
|
2008-10-31 14:29:42 -04:00
|
|
|
const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
|
|
|
|
DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
|
|
|
|
|
2009-05-06 14:36:50 -04:00
|
|
|
let chrome = null;
|
2008-10-31 14:09:20 -04:00
|
|
|
let panel = null;
|
2009-10-31 22:25:28 -04:00
|
|
|
let placesManager = null;
|
2009-08-11 07:46:10 -04:00
|
|
|
let overview = null;
|
2008-12-09 17:10:43 -05:00
|
|
|
let runDialog = null;
|
2009-08-02 03:46:01 -04:00
|
|
|
let lookingGlass = null;
|
2008-11-21 09:02:09 -05:00
|
|
|
let wm = null;
|
2010-01-13 15:05:20 -05:00
|
|
|
let messageTray = null;
|
2010-02-02 10:21:47 -05:00
|
|
|
let notificationDaemon = null;
|
2010-02-22 13:53:41 -05:00
|
|
|
let windowAttentionHandler = null;
|
2010-02-02 10:21:47 -05:00
|
|
|
let telepathyClient = null;
|
2010-07-01 14:13:42 -04:00
|
|
|
let ctrlAltTabManager = null;
|
2009-03-13 17:14:31 -04:00
|
|
|
let recorder = null;
|
2009-09-23 14:30:05 -04:00
|
|
|
let shellDBusService = null;
|
2009-09-15 15:53:07 -04:00
|
|
|
let modalCount = 0;
|
|
|
|
let modalActorFocusStack = [];
|
2010-05-06 17:18:10 -04:00
|
|
|
let uiGroup = null;
|
2010-05-10 12:40:03 -04:00
|
|
|
let magnifier = null;
|
2011-01-05 09:47:27 -05:00
|
|
|
let xdndHandler = null;
|
2010-08-09 12:33:34 -04:00
|
|
|
let statusIconDispatcher = null;
|
2009-10-24 13:36:52 -04:00
|
|
|
let _errorLogStack = [];
|
|
|
|
let _startDate;
|
2011-02-01 10:11:00 -05:00
|
|
|
let _defaultCssStylesheet = null;
|
|
|
|
let _cssStylesheet = null;
|
2008-10-31 14:09:20 -04:00
|
|
|
|
2010-02-03 14:47:52 -05:00
|
|
|
let background = null;
|
|
|
|
|
2008-10-31 00:22:44 -04:00
|
|
|
function start() {
|
2010-05-13 15:46:04 -04:00
|
|
|
// Add a binding for 'global' in the global JS namespace; (gjs
|
2009-09-08 16:12:50 -04:00
|
|
|
// keeps the web browser convention of having that namespace be
|
2010-05-13 15:46:04 -04:00
|
|
|
// called 'window'.)
|
2009-09-08 16:12:50 -04:00
|
|
|
window.global = Shell.Global.get();
|
2009-01-22 16:28:19 -05:00
|
|
|
|
2009-10-24 13:36:52 -04:00
|
|
|
// Now monkey patch utility functions into the global proxy;
|
|
|
|
// This is easier and faster than indirecting down into global
|
|
|
|
// if we want to call back up into JS.
|
|
|
|
global.logError = _logError;
|
|
|
|
global.log = _logDebug;
|
|
|
|
|
2010-05-13 15:46:04 -04:00
|
|
|
Gio.DesktopAppInfo.set_desktop_env('GNOME');
|
2009-01-22 16:28:19 -05:00
|
|
|
|
2009-01-19 18:21:57 -05:00
|
|
|
global.grab_dbus_service();
|
2009-09-23 14:30:05 -04:00
|
|
|
shellDBusService = new ShellDBus.GnomeShell();
|
|
|
|
// Force a connection now; dbus.js will do this internally
|
|
|
|
// if we use its name acquisition stuff but we aren't right
|
|
|
|
// now; to do so we'd need to convert from its async calls
|
|
|
|
// back into sync ones.
|
|
|
|
DBus.session.flush();
|
2008-10-31 14:29:42 -04:00
|
|
|
|
2009-09-18 15:51:15 -04:00
|
|
|
Environment.init();
|
2008-11-22 23:12:34 -05:00
|
|
|
|
2009-10-15 19:28:29 -04:00
|
|
|
// Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
|
2009-08-12 13:32:54 -04:00
|
|
|
// also initialize ShellAppSystem first. ShellAppSystem
|
2009-10-15 19:28:29 -04:00
|
|
|
// needs to load all the .desktop files, and ShellWindowTracker
|
2009-08-12 13:32:54 -04:00
|
|
|
// will use those to associate with windows. Right now
|
|
|
|
// the Monitor doesn't listen for installed app changes
|
|
|
|
// and recalculate application associations, so to avoid
|
|
|
|
// races for now we initialize it here. It's better to
|
|
|
|
// be predictable anyways.
|
2009-10-15 19:28:29 -04:00
|
|
|
Shell.WindowTracker.get_default();
|
|
|
|
Shell.AppUsage.get_default();
|
2009-08-12 13:32:54 -04:00
|
|
|
|
2010-11-13 13:02:39 -05:00
|
|
|
// The stage is always covered so Clutter doesn't need to clear it; however
|
2010-11-14 13:13:44 -05:00
|
|
|
// the color is used as the default contents for the Mutter root background
|
|
|
|
// actor so set it anyways.
|
2008-10-31 14:29:42 -04:00
|
|
|
global.stage.color = DEFAULT_BACKGROUND_COLOR;
|
2010-11-13 13:02:39 -05:00
|
|
|
global.stage.no_clear_hint = true;
|
2008-10-31 14:29:42 -04:00
|
|
|
|
2011-02-01 10:11:00 -05:00
|
|
|
_defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
|
2011-01-04 14:30:03 -05:00
|
|
|
loadTheme();
|
2009-09-10 01:36:41 -04:00
|
|
|
|
2009-09-24 22:53:16 -04:00
|
|
|
let shellwm = global.window_manager;
|
2010-05-13 15:46:04 -04:00
|
|
|
shellwm.takeover_keybinding('panel_main_menu');
|
|
|
|
shellwm.connect('keybinding::panel_main_menu', function () {
|
2009-09-24 22:53:16 -04:00
|
|
|
overview.toggle();
|
|
|
|
});
|
2010-05-13 15:46:04 -04:00
|
|
|
shellwm.takeover_keybinding('panel_run_dialog');
|
|
|
|
shellwm.connect('keybinding::panel_run_dialog', function () {
|
2009-09-24 22:53:16 -04:00
|
|
|
getRunDialog().open();
|
|
|
|
});
|
2008-11-07 13:42:23 -05:00
|
|
|
|
2010-05-06 17:18:10 -04:00
|
|
|
// Set up stage hierarchy to group all UI actors under one container.
|
|
|
|
uiGroup = new Clutter.Group();
|
|
|
|
global.window_group.reparent(uiGroup);
|
|
|
|
global.overlay_group.reparent(uiGroup);
|
|
|
|
global.stage.add_actor(uiGroup);
|
|
|
|
|
2009-10-31 22:25:28 -04:00
|
|
|
placesManager = new PlaceDisplay.PlacesManager();
|
2011-01-05 09:47:27 -05:00
|
|
|
xdndHandler = new XdndHandler.XdndHandler();
|
2009-08-11 07:46:10 -04:00
|
|
|
overview = new Overview.Overview();
|
2009-05-06 14:36:50 -04:00
|
|
|
chrome = new Chrome.Chrome();
|
2010-07-21 04:44:59 -04:00
|
|
|
magnifier = new Magnifier.Magnifier();
|
2010-08-09 12:33:34 -04:00
|
|
|
statusIconDispatcher = new StatusIconDispatcher.StatusIconDispatcher();
|
2008-10-31 14:09:20 -04:00
|
|
|
panel = new Panel.Panel();
|
2008-11-21 09:02:09 -05:00
|
|
|
wm = new WindowManager.WindowManager();
|
2010-02-02 10:21:47 -05:00
|
|
|
messageTray = new MessageTray.MessageTray();
|
2010-01-13 15:05:20 -05:00
|
|
|
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
2010-02-22 13:53:41 -05:00
|
|
|
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
2010-02-02 10:21:47 -05:00
|
|
|
telepathyClient = new TelepathyClient.Client();
|
2011-01-31 12:48:18 -05:00
|
|
|
|
2011-02-11 17:53:27 -05:00
|
|
|
overview.init();
|
2011-01-31 12:48:18 -05:00
|
|
|
statusIconDispatcher.start(messageTray.actor);
|
2009-08-12 13:32:54 -04:00
|
|
|
|
2010-07-01 14:13:42 -04:00
|
|
|
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
|
|
|
ctrlAltTabManager.addGroup(panel.actor, _("Panel"), 'gnome-panel');
|
|
|
|
|
2009-10-24 13:36:52 -04:00
|
|
|
_startDate = new Date();
|
|
|
|
|
2010-05-05 17:05:42 -04:00
|
|
|
let recorderSettings = new Gio.Settings({ schema: 'org.gnome.shell.recorder' });
|
|
|
|
|
2010-06-19 06:08:52 -04:00
|
|
|
global.screen.connect('toggle-recording', function() {
|
2009-03-13 17:14:31 -04:00
|
|
|
if (recorder == null) {
|
|
|
|
recorder = new Shell.Recorder({ stage: global.stage });
|
|
|
|
}
|
|
|
|
|
|
|
|
if (recorder.is_recording()) {
|
|
|
|
recorder.pause();
|
|
|
|
} else {
|
2010-05-05 17:05:42 -04:00
|
|
|
// read the parameters from GSettings always in case they have changed
|
|
|
|
recorder.set_framerate(recorderSettings.get_int('framerate'));
|
|
|
|
recorder.set_filename('shell-%d%u-%c.' + recorderSettings.get_string('file-extension'));
|
|
|
|
let pipeline = recorderSettings.get_string('pipeline');
|
|
|
|
|
2010-02-08 15:25:58 -05:00
|
|
|
if (!pipeline.match(/^\s*$/))
|
|
|
|
recorder.set_pipeline(pipeline);
|
|
|
|
else
|
|
|
|
recorder.set_pipeline(null);
|
|
|
|
|
2009-03-13 17:14:31 -04:00
|
|
|
recorder.record();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2011-01-30 18:21:31 -05:00
|
|
|
global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT, false, -1, 1);
|
|
|
|
|
2011-01-06 10:30:15 -05:00
|
|
|
// Provide the bus object for gnome-session to
|
|
|
|
// initiate logouts.
|
|
|
|
EndSessionDialog.init();
|
|
|
|
|
2011-02-21 12:17:34 -05:00
|
|
|
// Attempt to become a PolicyKit authentication agent
|
|
|
|
PolkitAuthenticationAgent.init()
|
|
|
|
|
2010-06-03 11:14:58 -04:00
|
|
|
global.gdk_screen.connect('monitors-changed', _relayout);
|
2009-08-11 11:16:25 -04:00
|
|
|
|
2009-10-25 18:53:10 -04:00
|
|
|
ExtensionSystem.init();
|
|
|
|
ExtensionSystem.loadExtensions();
|
|
|
|
|
2010-11-28 07:42:29 -05:00
|
|
|
panel.startStatusArea();
|
2009-07-02 00:52:21 -04:00
|
|
|
panel.startupAnimation();
|
|
|
|
|
2009-05-07 09:47:48 -04:00
|
|
|
let display = global.screen.get_display();
|
2009-08-11 07:46:10 -04:00
|
|
|
display.connect('overlay-key', Lang.bind(overview, overview.toggle));
|
2009-08-28 15:11:25 -04:00
|
|
|
|
|
|
|
global.stage.connect('captured-event', _globalKeyPressHandler);
|
|
|
|
|
2010-05-11 10:35:27 -04:00
|
|
|
// Perform initial relayout here
|
|
|
|
_relayout();
|
|
|
|
|
2009-10-24 13:36:52 -04:00
|
|
|
_log('info', 'loaded at ' + _startDate);
|
2010-01-08 11:27:52 -05:00
|
|
|
log('GNOME Shell started at ' + _startDate);
|
2009-10-24 13:36:52 -04:00
|
|
|
|
2010-05-09 13:42:35 -04:00
|
|
|
let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
|
|
|
|
if (perfModuleName) {
|
|
|
|
let perfOutput = GLib.getenv("SHELL_PERF_OUTPUT");
|
|
|
|
let module = eval('imports.perf.' + perfModuleName + ';');
|
|
|
|
Scripting.runPerfScript(module, perfOutput);
|
|
|
|
}
|
2011-01-25 16:29:45 -05:00
|
|
|
|
|
|
|
global.screen.connect('notify::n-workspaces', _nWorkspacesChanged);
|
|
|
|
Mainloop.idle_add(_nWorkspacesChanged);
|
|
|
|
}
|
|
|
|
|
|
|
|
let _workspaces = [];
|
|
|
|
let _checkWorkspacesId = 0;
|
|
|
|
|
|
|
|
function _checkWorkspaces() {
|
|
|
|
let i;
|
|
|
|
let emptyWorkspaces = [];
|
|
|
|
|
|
|
|
for (i = 0; i < _workspaces.length; i++)
|
|
|
|
emptyWorkspaces[i] = true;
|
|
|
|
|
|
|
|
let windows = global.get_window_actors();
|
|
|
|
for (i = 0; i < windows.length; i++) {
|
|
|
|
let win = windows[i];
|
|
|
|
|
|
|
|
if (win.get_meta_window().is_on_all_workspaces())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
let workspaceIndex = win.get_workspace();
|
|
|
|
emptyWorkspaces[workspaceIndex] = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we don't have an empty workspace at the end, add one
|
|
|
|
if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
|
|
|
|
global.screen.append_new_workspace(false, global.get_current_time());
|
|
|
|
emptyWorkspaces.push(false);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Delete other empty workspaces; do it from the end to avoid index changes
|
|
|
|
for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
|
|
|
|
if (emptyWorkspaces[i])
|
|
|
|
global.screen.remove_workspace(_workspaces[i], global.get_current_time());
|
|
|
|
}
|
|
|
|
|
|
|
|
_checkWorkspacesId = 0;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
function _queueCheckWorkspaces() {
|
|
|
|
if (_checkWorkspacesId == 0)
|
|
|
|
_checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces);
|
|
|
|
}
|
|
|
|
|
|
|
|
function _nWorkspacesChanged() {
|
|
|
|
let oldNumWorkspaces = _workspaces.length;
|
|
|
|
let newNumWorkspaces = global.screen.n_workspaces;
|
|
|
|
|
|
|
|
if (oldNumWorkspaces == newNumWorkspaces)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
let lostWorkspaces = [];
|
|
|
|
if (newNumWorkspaces > oldNumWorkspaces) {
|
|
|
|
let w;
|
|
|
|
|
|
|
|
// Assume workspaces are only added at the end
|
|
|
|
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
|
|
|
|
_workspaces[w] = global.screen.get_workspace_by_index(w);
|
|
|
|
|
|
|
|
for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
|
|
|
|
let workspace = _workspaces[w];
|
|
|
|
workspace._windowAddedId = workspace.connect('window-added', _queueCheckWorkspaces);
|
|
|
|
workspace._windowRemovedId = workspace.connect('window-removed', _queueCheckWorkspaces);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Assume workspaces are only removed sequentially
|
|
|
|
// (e.g. 2,3,4 - not 2,4,7)
|
|
|
|
let removedIndex;
|
|
|
|
let removedNum = oldNumWorkspaces - newNumWorkspaces;
|
|
|
|
for (let w = 0; w < oldNumWorkspaces; w++) {
|
|
|
|
let workspace = global.screen.get_workspace_by_index(w);
|
|
|
|
if (_workspaces[w] != workspace) {
|
|
|
|
removedIndex = w;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let lostWorkspaces = _workspaces.splice(removedIndex, removedNum);
|
|
|
|
lostWorkspaces.forEach(function(workspace) {
|
|
|
|
workspace.disconnect(workspace._windowAddedId);
|
|
|
|
workspace.disconnect(workspace._windowRemovedId);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
_queueCheckWorkspaces();
|
|
|
|
|
|
|
|
return false;
|
2009-02-16 14:32:04 -05:00
|
|
|
}
|
|
|
|
|
2011-01-11 06:15:26 -05:00
|
|
|
/**
|
2011-02-01 09:44:15 -05:00
|
|
|
* getThemeStylesheet:
|
|
|
|
*
|
|
|
|
* Get the theme CSS file that the shell will load
|
|
|
|
*
|
|
|
|
* Returns: A file path that contains the theme CSS,
|
|
|
|
* null if using the default
|
|
|
|
*/
|
|
|
|
function getThemeStylesheet()
|
|
|
|
{
|
2011-02-01 10:11:00 -05:00
|
|
|
return _cssStylesheet;
|
2011-02-01 09:44:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* setThemeStylesheet:
|
2011-02-01 10:11:00 -05:00
|
|
|
* @cssStylesheet: A file path that contains the theme CSS,
|
2011-01-11 06:15:26 -05:00
|
|
|
* set it to null to use the default
|
|
|
|
*
|
|
|
|
* Set the theme CSS file that the shell will load
|
|
|
|
*/
|
2011-02-01 10:11:00 -05:00
|
|
|
function setThemeStylesheet(cssStylesheet)
|
2011-01-11 06:15:26 -05:00
|
|
|
{
|
2011-02-01 10:11:00 -05:00
|
|
|
_cssStylesheet = cssStylesheet;
|
2011-01-11 06:15:26 -05:00
|
|
|
}
|
|
|
|
|
2011-01-04 14:30:03 -05:00
|
|
|
/**
|
|
|
|
* loadTheme:
|
|
|
|
*
|
2011-01-11 06:15:26 -05:00
|
|
|
* Reloads the theme CSS file
|
2011-01-04 14:30:03 -05:00
|
|
|
*/
|
|
|
|
function loadTheme() {
|
|
|
|
let themeContext = St.ThemeContext.get_for_stage (global.stage);
|
2011-01-11 06:15:26 -05:00
|
|
|
|
2011-02-01 10:11:00 -05:00
|
|
|
let cssStylesheet = _defaultCssStylesheet;
|
|
|
|
if (_cssStylesheet != null)
|
|
|
|
cssStylesheet = _cssStylesheet;
|
2011-01-11 06:15:26 -05:00
|
|
|
|
2011-02-01 10:11:00 -05:00
|
|
|
let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
|
2011-01-04 14:30:03 -05:00
|
|
|
themeContext.set_theme (theme);
|
|
|
|
}
|
|
|
|
|
2009-10-24 13:36:52 -04:00
|
|
|
/**
|
|
|
|
* _log:
|
|
|
|
* @category: string message type ('info', 'error')
|
|
|
|
* @msg: A message string
|
|
|
|
* ...: Any further arguments are converted into JSON notation,
|
|
|
|
* and appended to the log message, separated by spaces.
|
|
|
|
*
|
|
|
|
* Log a message into the LookingGlass error
|
|
|
|
* stream. This is primarily intended for use by the
|
|
|
|
* extension system as well as debugging.
|
|
|
|
*/
|
|
|
|
function _log(category, msg) {
|
|
|
|
let text = msg;
|
|
|
|
if (arguments.length > 2) {
|
|
|
|
text += ': ';
|
|
|
|
for (let i = 2; i < arguments.length; i++) {
|
|
|
|
text += JSON.stringify(arguments[i]);
|
|
|
|
if (i < arguments.length - 1)
|
2010-05-13 15:46:04 -04:00
|
|
|
text += ' ';
|
2009-10-24 13:36:52 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_errorLogStack.push({timestamp: new Date().getTime(),
|
|
|
|
category: category,
|
|
|
|
message: text });
|
|
|
|
}
|
|
|
|
|
|
|
|
function _logError(msg) {
|
|
|
|
return _log('error', msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
function _logDebug(msg) {
|
|
|
|
return _log('debug', msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Used by the error display in lookingGlass.js
|
|
|
|
function _getAndClearErrorStack() {
|
|
|
|
let errors = _errorLogStack;
|
|
|
|
_errorLogStack = [];
|
|
|
|
return errors;
|
|
|
|
}
|
|
|
|
|
2009-08-11 11:16:25 -04:00
|
|
|
function _relayout() {
|
2009-08-25 15:23:53 -04:00
|
|
|
let primary = global.get_primary_monitor();
|
|
|
|
panel.actor.set_position(primary.x, primary.y);
|
|
|
|
panel.actor.set_size(primary.width, Panel.PANEL_HEIGHT);
|
2009-08-11 11:16:25 -04:00
|
|
|
overview.relayout();
|
2010-01-19 12:59:29 -05:00
|
|
|
|
|
|
|
// To avoid updating the position and size of the workspaces
|
|
|
|
// in the overview, we just hide the overview. The positions
|
2011-01-28 16:35:46 -05:00
|
|
|
// will be updated when it is next shown.
|
2010-01-19 12:59:29 -05:00
|
|
|
overview.hide();
|
2009-08-11 11:16:25 -04:00
|
|
|
}
|
|
|
|
|
2011-02-07 20:05:30 -05:00
|
|
|
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
|
|
|
|
return win.get_workspace() == workspaceIndex ||
|
|
|
|
(win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
|
|
|
|
}
|
|
|
|
|
|
|
|
function getWindowActorsForWorkspace(workspaceIndex) {
|
|
|
|
return global.get_window_actors().filter(function (win) {
|
|
|
|
return isWindowActorDisplayedOnWorkspace(win, workspaceIndex);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2009-08-28 15:11:25 -04:00
|
|
|
// This function encapsulates hacks to make certain global keybindings
|
|
|
|
// work even when we are in one of our modes where global keybindings
|
|
|
|
// are disabled with a global grab. (When there is a global grab, then
|
|
|
|
// all key events will be delivered to the stage, so ::captured-event
|
|
|
|
// on the stage can be used for global keybindings.)
|
|
|
|
function _globalKeyPressHandler(actor, event) {
|
2009-09-15 15:53:07 -04:00
|
|
|
if (modalCount == 0)
|
2009-08-28 15:11:25 -04:00
|
|
|
return false;
|
2011-01-06 14:39:07 -05:00
|
|
|
if (event.type() != Clutter.EventType.KEY_PRESS)
|
2010-10-14 10:38:33 -04:00
|
|
|
return false;
|
|
|
|
|
|
|
|
let symbol = event.get_key_symbol();
|
|
|
|
let keyCode = event.get_key_code();
|
|
|
|
let modifierState = Shell.get_event_state(event);
|
|
|
|
|
2011-01-06 15:30:24 -05:00
|
|
|
let display = global.screen.get_display();
|
|
|
|
// This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
|
|
|
|
let action = display.get_keybinding_action(keyCode, modifierState);
|
|
|
|
|
|
|
|
// The screenshot action should always be available (even if a
|
|
|
|
// modal dialog is present)
|
|
|
|
if (action == Meta.KeyBindingAction.COMMAND_SCREENSHOT) {
|
|
|
|
let gconf = GConf.Client.get_default();
|
|
|
|
let command = gconf.get_string('/apps/metacity/keybinding_commands/command_screenshot');
|
2010-11-17 11:43:08 -05:00
|
|
|
if (command != null && command != '')
|
|
|
|
Util.spawnCommandLine(command);
|
2010-10-14 10:38:33 -04:00
|
|
|
return true;
|
|
|
|
}
|
2009-08-28 15:11:25 -04:00
|
|
|
|
2011-01-06 15:30:24 -05:00
|
|
|
// Other bindings are only available when the overview is up and
|
|
|
|
// no modal dialog is present.
|
|
|
|
if (!overview.visible || modalCount > 1)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
// This isn't a Meta.KeyBindingAction yet
|
|
|
|
if (symbol == Clutter.Super_L || symbol == Clutter.Super_R) {
|
|
|
|
overview.hide();
|
|
|
|
return true;
|
|
|
|
}
|
2009-08-28 15:11:25 -04:00
|
|
|
|
2010-10-14 10:38:33 -04:00
|
|
|
switch (action) {
|
2011-02-09 11:43:58 -05:00
|
|
|
// left/right would effectively act as synonyms for up/down if we enabled them;
|
|
|
|
// but that could be considered confusing; we also disable them in the main view.
|
|
|
|
//
|
|
|
|
// case Meta.KeyBindingAction.WORKSPACE_LEFT:
|
|
|
|
// wm.actionMoveWorkspaceLeft();
|
|
|
|
// return true;
|
|
|
|
// case Meta.KeyBindingAction.WORKSPACE_RIGHT:
|
|
|
|
// wm.actionMoveWorkspaceRight();
|
|
|
|
// return true;
|
2011-01-30 18:21:31 -05:00
|
|
|
case Meta.KeyBindingAction.WORKSPACE_UP:
|
|
|
|
wm.actionMoveWorkspaceUp();
|
|
|
|
return true;
|
|
|
|
case Meta.KeyBindingAction.WORKSPACE_DOWN:
|
|
|
|
wm.actionMoveWorkspaceDown();
|
|
|
|
return true;
|
2010-10-14 10:38:33 -04:00
|
|
|
case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
|
|
|
|
case Meta.KeyBindingAction.COMMAND_2:
|
|
|
|
getRunDialog().open();
|
2009-08-28 15:11:25 -04:00
|
|
|
return true;
|
2011-01-06 14:39:07 -05:00
|
|
|
case Meta.KeyBindingAction.PANEL_MAIN_MENU:
|
2011-01-06 15:30:24 -05:00
|
|
|
overview.hide();
|
2011-01-06 14:39:07 -05:00
|
|
|
return true;
|
2010-07-01 14:13:42 -04:00
|
|
|
case Meta.KeyBindingAction.SWITCH_PANELS:
|
2011-01-06 15:30:24 -05:00
|
|
|
ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK);
|
|
|
|
return true;
|
2009-08-28 15:11:25 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2009-09-15 15:53:07 -04:00
|
|
|
function _findModal(actor) {
|
|
|
|
for (let i = 0; i < modalActorFocusStack.length; i++) {
|
|
|
|
let [stackActor, stackFocus] = modalActorFocusStack[i];
|
|
|
|
if (stackActor == actor) {
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* pushModal:
|
|
|
|
* @actor: #ClutterActor which will be given keyboard focus
|
2010-10-20 15:08:14 -04:00
|
|
|
* @timestamp: optional timestamp
|
2009-09-15 15:53:07 -04:00
|
|
|
*
|
|
|
|
* Ensure we are in a mode where all keyboard and mouse input goes to
|
2010-11-03 13:30:08 -04:00
|
|
|
* the stage, and focus @actor. Multiple calls to this function act in
|
|
|
|
* a stacking fashion; the effect will be undone when an equal number
|
|
|
|
* of popModal() invocations have been made.
|
2009-09-15 15:53:07 -04:00
|
|
|
*
|
2010-11-03 13:30:08 -04:00
|
|
|
* Next, record the current Clutter keyboard focus on a stack. If the
|
|
|
|
* modal stack returns to this actor, reset the focus to the actor
|
|
|
|
* which was focused at the time pushModal() was invoked.
|
2009-09-16 11:37:51 -04:00
|
|
|
*
|
2010-10-20 15:08:14 -04:00
|
|
|
* @timestamp is optionally used to associate the call with a specific user
|
|
|
|
* initiated event. If not provided then the value of
|
|
|
|
* global.get_current_time() is assumed.
|
|
|
|
*
|
2009-09-16 11:37:51 -04:00
|
|
|
* Returns: true iff we successfully acquired a grab or already had one
|
2009-09-15 15:53:07 -04:00
|
|
|
*/
|
2010-10-20 15:08:14 -04:00
|
|
|
function pushModal(actor, timestamp) {
|
|
|
|
|
|
|
|
if (timestamp == undefined)
|
|
|
|
timestamp = global.get_current_time();
|
|
|
|
|
2009-09-16 11:37:51 -04:00
|
|
|
if (modalCount == 0) {
|
2010-10-20 15:08:14 -04:00
|
|
|
if (!global.begin_modal(timestamp)) {
|
2010-05-13 15:46:04 -04:00
|
|
|
log('pushModal: invocation of begin_modal failed');
|
2009-09-16 11:37:51 -04:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
|
|
|
|
|
2009-09-15 15:53:07 -04:00
|
|
|
modalCount += 1;
|
|
|
|
actor.connect('destroy', function() {
|
|
|
|
let index = _findModal(actor);
|
|
|
|
if (index >= 0)
|
|
|
|
modalActorFocusStack.splice(index, 1);
|
|
|
|
});
|
|
|
|
let curFocus = global.stage.get_key_focus();
|
|
|
|
if (curFocus != null) {
|
|
|
|
curFocus.connect('destroy', function() {
|
|
|
|
let index = _findModal(actor);
|
|
|
|
if (index >= 0)
|
|
|
|
modalActorFocusStack[index][1] = null;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
modalActorFocusStack.push([actor, curFocus]);
|
2008-11-24 14:07:18 -05:00
|
|
|
|
2010-11-03 13:30:08 -04:00
|
|
|
global.stage.set_key_focus(actor);
|
2009-09-16 11:37:51 -04:00
|
|
|
return true;
|
2008-10-31 19:09:46 -04:00
|
|
|
}
|
|
|
|
|
2009-09-15 15:53:07 -04:00
|
|
|
/**
|
|
|
|
* popModal:
|
|
|
|
* @actor: #ClutterActor passed to original invocation of pushModal().
|
2010-10-20 15:08:14 -04:00
|
|
|
* @timestamp: optional timestamp
|
2009-09-15 15:53:07 -04:00
|
|
|
*
|
|
|
|
* Reverse the effect of pushModal(). If this invocation is undoing
|
|
|
|
* the topmost invocation, then the focus will be restored to the
|
|
|
|
* previous focus at the time when pushModal() was invoked.
|
2010-10-20 15:08:14 -04:00
|
|
|
*
|
|
|
|
* @timestamp is optionally used to associate the call with a specific user
|
|
|
|
* initiated event. If not provided then the value of
|
|
|
|
* global.get_current_time() is assumed.
|
2009-09-15 15:53:07 -04:00
|
|
|
*/
|
2010-10-20 15:08:14 -04:00
|
|
|
function popModal(actor, timestamp) {
|
|
|
|
|
|
|
|
if (timestamp == undefined)
|
|
|
|
timestamp = global.get_current_time();
|
|
|
|
|
2009-09-15 15:53:07 -04:00
|
|
|
modalCount -= 1;
|
|
|
|
let focusIndex = _findModal(actor);
|
|
|
|
if (focusIndex >= 0) {
|
|
|
|
if (focusIndex == modalActorFocusStack.length - 1) {
|
|
|
|
let [stackActor, stackFocus] = modalActorFocusStack[focusIndex];
|
|
|
|
global.stage.set_key_focus(stackFocus);
|
|
|
|
} else {
|
|
|
|
// Remove from the middle, shift the focus chain up
|
|
|
|
for (let i = focusIndex; i < modalActorFocusStack.length - 1; i++) {
|
|
|
|
modalActorFocusStack[i + 1][1] = modalActorFocusStack[i][1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
modalActorFocusStack.splice(focusIndex, 1);
|
|
|
|
}
|
|
|
|
if (modalCount > 0)
|
|
|
|
return;
|
|
|
|
|
2010-11-03 13:30:08 -04:00
|
|
|
global.stage.set_key_focus(null);
|
2010-10-20 15:08:14 -04:00
|
|
|
global.end_modal(timestamp);
|
2009-04-29 14:01:09 -04:00
|
|
|
global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
|
2008-10-31 00:22:44 -04:00
|
|
|
}
|
2008-11-24 14:07:18 -05:00
|
|
|
|
2009-08-02 03:46:01 -04:00
|
|
|
function createLookingGlass() {
|
|
|
|
if (lookingGlass == null) {
|
|
|
|
lookingGlass = new LookingGlass.LookingGlass();
|
|
|
|
lookingGlass.slaveTo(panel.actor);
|
|
|
|
}
|
|
|
|
return lookingGlass;
|
|
|
|
}
|
|
|
|
|
2009-09-14 15:08:20 -04:00
|
|
|
function getRunDialog() {
|
|
|
|
if (runDialog == null) {
|
|
|
|
runDialog = new RunDialog.RunDialog();
|
|
|
|
}
|
|
|
|
return runDialog;
|
|
|
|
}
|
|
|
|
|
2009-09-21 16:29:37 -04:00
|
|
|
/**
|
|
|
|
* activateWindow:
|
|
|
|
* @window: the Meta.Window to activate
|
|
|
|
* @time: (optional) current event time
|
2010-02-19 11:42:36 -05:00
|
|
|
* @workspaceNum: (optional) window's workspace number
|
2009-09-21 16:29:37 -04:00
|
|
|
*
|
2010-02-17 14:05:06 -05:00
|
|
|
* Activates @window, switching to its workspace first if necessary,
|
|
|
|
* and switching out of the overview if it's currently active
|
2009-09-21 16:29:37 -04:00
|
|
|
*/
|
2010-02-19 11:42:36 -05:00
|
|
|
function activateWindow(window, time, workspaceNum) {
|
2009-09-21 16:29:37 -04:00
|
|
|
let activeWorkspaceNum = global.screen.get_active_workspace_index();
|
2010-02-19 11:42:36 -05:00
|
|
|
let windowWorkspaceNum = (workspaceNum !== undefined) ? workspaceNum : window.get_workspace().index();
|
2009-09-21 16:29:37 -04:00
|
|
|
|
|
|
|
if (!time)
|
2009-12-03 15:59:52 -05:00
|
|
|
time = global.get_current_time();
|
2009-09-21 16:29:37 -04:00
|
|
|
|
|
|
|
if (windowWorkspaceNum != activeWorkspaceNum) {
|
|
|
|
let workspace = global.screen.get_workspace_by_index(windowWorkspaceNum);
|
|
|
|
workspace.activate_with_focus(window, time);
|
|
|
|
} else {
|
|
|
|
window.activate(time);
|
|
|
|
}
|
2010-02-17 14:05:06 -05:00
|
|
|
|
|
|
|
overview.hide();
|
2009-09-21 16:29:37 -04:00
|
|
|
}
|
2009-12-03 12:19:38 -05:00
|
|
|
|
|
|
|
// TODO - replace this timeout with some system to guess when the user might
|
|
|
|
// be e.g. just reading the screen and not likely to interact.
|
|
|
|
const DEFERRED_TIMEOUT_SECONDS = 20;
|
|
|
|
var _deferredWorkData = {};
|
|
|
|
// Work scheduled for some point in the future
|
|
|
|
var _deferredWorkQueue = [];
|
|
|
|
// Work we need to process before the next redraw
|
|
|
|
var _beforeRedrawQueue = [];
|
|
|
|
// Counter to assign work ids
|
|
|
|
var _deferredWorkSequence = 0;
|
|
|
|
var _deferredTimeoutId = 0;
|
|
|
|
|
|
|
|
function _runDeferredWork(workId) {
|
|
|
|
if (!_deferredWorkData[workId])
|
|
|
|
return;
|
|
|
|
let index = _deferredWorkQueue.indexOf(workId);
|
|
|
|
if (index < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_deferredWorkQueue.splice(index, 1);
|
|
|
|
_deferredWorkData[workId].callback();
|
|
|
|
if (_deferredWorkQueue.length == 0 && _deferredTimeoutId > 0) {
|
|
|
|
Mainloop.source_remove(_deferredTimeoutId);
|
|
|
|
_deferredTimeoutId = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function _runAllDeferredWork() {
|
|
|
|
while (_deferredWorkQueue.length > 0)
|
|
|
|
_runDeferredWork(_deferredWorkQueue[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
function _runBeforeRedrawQueue() {
|
|
|
|
for (let i = 0; i < _beforeRedrawQueue.length; i++) {
|
|
|
|
let workId = _beforeRedrawQueue[i];
|
|
|
|
_runDeferredWork(workId);
|
|
|
|
}
|
|
|
|
_beforeRedrawQueue = [];
|
|
|
|
}
|
|
|
|
|
|
|
|
function _queueBeforeRedraw(workId) {
|
|
|
|
_beforeRedrawQueue.push(workId);
|
|
|
|
if (_beforeRedrawQueue.length == 1) {
|
|
|
|
Meta.later_add(Meta.LaterType.BEFORE_REDRAW, function () {
|
|
|
|
_runBeforeRedrawQueue();
|
|
|
|
return false;
|
2010-04-12 18:05:50 -04:00
|
|
|
});
|
2009-12-03 12:19:38 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* initializeDeferredWork:
|
|
|
|
* @actor: A #ClutterActor
|
|
|
|
* @callback: Function to invoke to perform work
|
|
|
|
*
|
|
|
|
* This function sets up a callback to be invoked when either the
|
|
|
|
* given actor is mapped, or after some period of time when the machine
|
|
|
|
* is idle. This is useful if your actor isn't always visible on the
|
|
|
|
* screen (for example, all actors in the overview), and you don't want
|
|
|
|
* to consume resources updating if the actor isn't actually going to be
|
|
|
|
* displaying to the user.
|
|
|
|
*
|
|
|
|
* Note that queueDeferredWork is called by default immediately on
|
|
|
|
* initialization as well, under the assumption that new actors
|
|
|
|
* will need it.
|
|
|
|
*
|
|
|
|
* Returns: A string work identifer
|
|
|
|
*/
|
|
|
|
function initializeDeferredWork(actor, callback, props) {
|
|
|
|
// Turn into a string so we can use as an object property
|
2010-05-13 15:46:04 -04:00
|
|
|
let workId = '' + (++_deferredWorkSequence);
|
2009-12-03 12:19:38 -05:00
|
|
|
_deferredWorkData[workId] = { 'actor': actor,
|
|
|
|
'callback': callback };
|
|
|
|
actor.connect('notify::mapped', function () {
|
|
|
|
if (!(actor.mapped && _deferredWorkQueue.indexOf(workId) >= 0))
|
|
|
|
return;
|
|
|
|
_queueBeforeRedraw(workId);
|
|
|
|
});
|
|
|
|
actor.connect('destroy', function() {
|
|
|
|
let index = _deferredWorkQueue.indexOf(workId);
|
|
|
|
if (index >= 0)
|
|
|
|
_deferredWorkQueue.splice(index, 1);
|
|
|
|
delete _deferredWorkData[workId];
|
|
|
|
});
|
|
|
|
queueDeferredWork(workId);
|
|
|
|
return workId;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* queueDeferredWork:
|
|
|
|
* @workId: work identifier
|
|
|
|
*
|
|
|
|
* Ensure that the work identified by @workId will be
|
|
|
|
* run on map or timeout. You should call this function
|
|
|
|
* for example when data being displayed by the actor has
|
|
|
|
* changed.
|
|
|
|
*/
|
|
|
|
function queueDeferredWork(workId) {
|
|
|
|
let data = _deferredWorkData[workId];
|
|
|
|
if (!data) {
|
2010-05-13 15:46:04 -04:00
|
|
|
global.logError('invalid work id ', workId);
|
2009-12-03 12:19:38 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (_deferredWorkQueue.indexOf(workId) < 0)
|
|
|
|
_deferredWorkQueue.push(workId);
|
|
|
|
if (data.actor.mapped) {
|
|
|
|
_queueBeforeRedraw(workId);
|
|
|
|
return;
|
|
|
|
} else if (_deferredTimeoutId == 0) {
|
|
|
|
_deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
|
|
|
|
_runAllDeferredWork();
|
|
|
|
_deferredTimeoutId = 0;
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|