2011-09-28 09:16:26 -04:00
|
|
|
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
|
2008-10-31 14:09:20 -04:00
|
|
|
|
2008-10-31 00:22:44 -04:00
|
|
|
const Clutter = imports.gi.Clutter;
|
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;
|
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;
|
2008-10-31 00:22:44 -04:00
|
|
|
|
2012-09-02 21:23:50 -04:00
|
|
|
const Components = imports.ui.components;
|
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;
|
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;
|
2012-05-25 19:07:31 -04:00
|
|
|
const ExtensionDownloader = imports.ui.extensionDownloader;
|
2011-08-29 11:11:22 -04:00
|
|
|
const Keyboard = imports.ui.keyboard;
|
2010-01-13 15:05:20 -05:00
|
|
|
const MessageTray = imports.ui.messageTray;
|
2013-03-02 04:48:25 -05:00
|
|
|
const OsdWindow = imports.ui.osdWindow;
|
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;
|
2012-08-10 11:54:39 -04:00
|
|
|
const Params = imports.misc.params;
|
2008-12-09 17:10:43 -05:00
|
|
|
const RunDialog = imports.ui.runDialog;
|
2011-06-13 09:54:05 -04:00
|
|
|
const Layout = imports.ui.layout;
|
2013-03-04 13:20:12 -05:00
|
|
|
const LoginManager = imports.misc.loginManager;
|
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;
|
2012-02-06 17:28:48 -05:00
|
|
|
const ScreenShield = imports.ui.screenShield;
|
2010-05-09 13:42:35 -04:00
|
|
|
const Scripting = imports.ui.scripting;
|
2012-05-16 18:26:44 -04:00
|
|
|
const SessionMode = imports.ui.sessionMode;
|
2009-09-23 14:30:05 -04:00
|
|
|
const ShellDBus = imports.ui.shellDBus;
|
2012-06-20 17:23:34 -04:00
|
|
|
const ShellMountOperation = imports.ui.shellMountOperation;
|
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-11-17 11:43:08 -05:00
|
|
|
const Util = imports.misc.util;
|
2008-10-31 14:09:20 -04:00
|
|
|
|
2012-09-07 03:35:04 -04:00
|
|
|
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
|
2008-10-31 14:29:42 -04:00
|
|
|
|
2013-04-17 19:59:02 -04:00
|
|
|
const A11Y_SCHEMA = 'org.gnome.desktop.a11y.keyboard';
|
|
|
|
const STICKY_KEYS_ENABLE = 'stickykeys-enable';
|
|
|
|
|
2012-09-02 21:23:50 -04:00
|
|
|
let componentManager = null;
|
2008-10-31 14:09:20 -04:00
|
|
|
let panel = 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;
|
2012-02-06 17:28:48 -05:00
|
|
|
let screenShield = null;
|
2010-02-02 10:21:47 -05:00
|
|
|
let notificationDaemon = null;
|
2010-02-22 13:53:41 -05:00
|
|
|
let windowAttentionHandler = null;
|
2010-07-01 14:13:42 -04:00
|
|
|
let ctrlAltTabManager = null;
|
2013-03-02 04:48:25 -05:00
|
|
|
let osdWindow = null;
|
2012-05-16 18:26:44 -04:00
|
|
|
let sessionMode = null;
|
2009-09-23 14:30:05 -04:00
|
|
|
let shellDBusService = null;
|
2012-06-20 17:23:34 -04:00
|
|
|
let shellMountOpDBusService = null;
|
2012-06-02 19:20:31 -04:00
|
|
|
let screenSaverDBus = null;
|
2009-09-15 15:53:07 -04:00
|
|
|
let modalCount = 0;
|
2013-03-04 08:07:14 -05:00
|
|
|
let keybindingMode = Shell.KeyBindingMode.NONE;
|
2009-09-15 15:53:07 -04:00
|
|
|
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;
|
2011-08-29 11:11:22 -04:00
|
|
|
let keyboard = null;
|
2011-06-13 09:54:05 -04:00
|
|
|
let layoutManager = null;
|
2009-10-24 13:36:52 -04:00
|
|
|
let _startDate;
|
2011-02-01 10:11:00 -05:00
|
|
|
let _defaultCssStylesheet = null;
|
|
|
|
let _cssStylesheet = null;
|
2013-04-17 19:59:02 -04:00
|
|
|
let _a11ySettings = null;
|
2013-06-06 12:47:56 -04:00
|
|
|
let dynamicWorkspacesSchema = null;
|
2008-10-31 14:09:20 -04:00
|
|
|
|
2012-09-01 08:42:53 -04:00
|
|
|
function _sessionUpdated() {
|
2013-02-18 05:23:08 -05:00
|
|
|
_loadDefaultStylesheet();
|
|
|
|
|
2012-08-11 00:34:53 -04:00
|
|
|
wm.setCustomKeybindingHandler('panel-main-menu',
|
2012-12-12 08:14:13 -05:00
|
|
|
Shell.KeyBindingMode.NORMAL |
|
|
|
|
Shell.KeyBindingMode.OVERVIEW,
|
2012-08-11 00:34:53 -04:00
|
|
|
sessionMode.hasOverview ? Lang.bind(overview, overview.toggle) : null);
|
2012-12-12 08:14:13 -05:00
|
|
|
wm.allowKeybinding('overlay-key', Shell.KeyBindingMode.NORMAL |
|
|
|
|
Shell.KeyBindingMode.OVERVIEW);
|
2012-08-11 00:34:53 -04:00
|
|
|
|
|
|
|
wm.setCustomKeybindingHandler('panel-run-dialog',
|
2012-12-12 08:14:13 -05:00
|
|
|
Shell.KeyBindingMode.NORMAL |
|
|
|
|
Shell.KeyBindingMode.OVERVIEW,
|
2012-08-11 00:34:53 -04:00
|
|
|
sessionMode.hasRunDialog ? openRunDialog : null);
|
2013-03-08 16:59:02 -05:00
|
|
|
|
|
|
|
if (!sessionMode.hasRunDialog && lookingGlass)
|
|
|
|
lookingGlass.close();
|
2012-09-01 08:42:53 -04:00
|
|
|
}
|
|
|
|
|
2008-10-31 00:22:44 -04:00
|
|
|
function start() {
|
2012-04-29 16:42:45 -04:00
|
|
|
// These are here so we don't break compatibility.
|
|
|
|
global.logError = window.log;
|
|
|
|
global.log = window.log;
|
2009-10-24 13:36:52 -04:00
|
|
|
|
2011-03-15 18:31:16 -04:00
|
|
|
// Chain up async errors reported from C
|
|
|
|
global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
|
|
|
|
|
2010-05-13 15:46:04 -04:00
|
|
|
Gio.DesktopAppInfo.set_desktop_env('GNOME');
|
2009-01-22 16:28:19 -05:00
|
|
|
|
2013-02-18 05:26:25 -05:00
|
|
|
sessionMode = new SessionMode.SessionMode();
|
|
|
|
sessionMode.connect('sessions-loaded', _sessionsLoaded);
|
|
|
|
sessionMode.init();
|
2013-02-18 05:26:25 -05:00
|
|
|
}
|
2008-10-31 14:29:42 -04:00
|
|
|
|
2013-02-18 05:26:25 -05:00
|
|
|
function _sessionsLoaded() {
|
|
|
|
sessionMode.connect('updated', _sessionUpdated);
|
2013-06-06 10:39:23 -04:00
|
|
|
_initializePrefs();
|
2013-02-18 05:26:25 -05:00
|
|
|
_initializeUI();
|
2013-03-08 15:04:15 -05:00
|
|
|
|
|
|
|
shellDBusService = new ShellDBus.GnomeShell();
|
|
|
|
shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
|
|
|
|
|
2013-02-18 05:26:25 -05:00
|
|
|
_sessionUpdated();
|
|
|
|
}
|
2013-03-06 17:25:28 -05:00
|
|
|
|
2013-06-06 10:39:23 -04:00
|
|
|
function _initializePrefs() {
|
2013-06-06 10:40:02 -04:00
|
|
|
let keys = new Gio.Settings({ schema: sessionMode.overridesSchema }).list_keys();
|
2013-06-06 10:39:23 -04:00
|
|
|
for (let i = 0; i < keys.length; i++)
|
2013-06-06 10:40:02 -04:00
|
|
|
Meta.prefs_override_preference_schema(keys[i], sessionMode.overridesSchema);
|
2013-06-06 12:47:56 -04:00
|
|
|
|
|
|
|
if (keys.indexOf('dynamic-workspaces') > -1)
|
|
|
|
dynamicWorkspacesSchema = sessionMode.overridesSchema;
|
|
|
|
else
|
|
|
|
dynamicWorkspacesSchema = 'org.gnome.mutter';
|
2013-06-06 10:39:23 -04:00
|
|
|
}
|
|
|
|
|
2013-02-18 05:26:25 -05:00
|
|
|
function _initializeUI() {
|
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.
|
2012-12-22 22:10:27 -05:00
|
|
|
Shell.WindowTracker.get_default();
|
2009-10-15 19:28:29 -04:00
|
|
|
Shell.AppUsage.get_default();
|
2009-08-12 13:32:54 -04:00
|
|
|
|
2013-02-04 22:13:44 -05:00
|
|
|
_loadDefaultStylesheet();
|
2009-09-10 01:36:41 -04:00
|
|
|
|
2013-01-10 16:02:20 -05:00
|
|
|
// Setup the stage hierarchy early
|
2011-06-13 09:54:05 -04:00
|
|
|
layoutManager = new Layout.LayoutManager();
|
2013-02-13 22:45:43 -05:00
|
|
|
|
2013-01-10 16:02:20 -05:00
|
|
|
// Various parts of the codebase still refers to Main.uiGroup
|
|
|
|
// instead using the layoutManager. This keeps that code
|
|
|
|
// working until it's updated.
|
|
|
|
uiGroup = layoutManager.uiGroup;
|
|
|
|
|
2011-01-05 09:47:27 -05:00
|
|
|
xdndHandler = new XdndHandler.XdndHandler();
|
2011-02-07 11:29:34 -05:00
|
|
|
ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
|
2013-03-02 04:48:25 -05:00
|
|
|
osdWindow = new OsdWindow.OsdWindow();
|
2012-05-16 18:59:02 -04:00
|
|
|
overview = new Overview.Overview();
|
2012-11-29 19:27:16 -05:00
|
|
|
wm = new WindowManager.WindowManager();
|
2010-07-21 04:44:59 -04:00
|
|
|
magnifier = new Magnifier.Magnifier();
|
2013-03-04 13:20:12 -05:00
|
|
|
if (LoginManager.canLock())
|
2012-07-08 11:42:15 -04:00
|
|
|
screenShield = new ScreenShield.ScreenShield();
|
2013-02-16 23:28:29 -05:00
|
|
|
|
2008-10-31 14:09:20 -04:00
|
|
|
panel = new Panel.Panel();
|
2010-02-02 10:21:47 -05:00
|
|
|
messageTray = new MessageTray.MessageTray();
|
2011-08-29 11:11:22 -04:00
|
|
|
keyboard = new Keyboard.Keyboard();
|
2010-01-13 15:05:20 -05:00
|
|
|
notificationDaemon = new NotificationDaemon.NotificationDaemon();
|
2010-02-22 13:53:41 -05:00
|
|
|
windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
|
2012-09-02 21:23:50 -04:00
|
|
|
componentManager = new Components.ComponentManager();
|
2011-01-31 12:48:18 -05:00
|
|
|
|
2011-09-01 13:35:15 -04:00
|
|
|
layoutManager.init();
|
2011-08-23 21:53:02 -04:00
|
|
|
overview.init();
|
2009-03-13 17:14:31 -04:00
|
|
|
|
2013-04-17 19:59:02 -04:00
|
|
|
_a11ySettings = new Gio.Settings({ schema: A11Y_SCHEMA });
|
|
|
|
|
|
|
|
global.display.connect('overlay-key', Lang.bind(overview, function () {
|
|
|
|
if (!_a11ySettings.get_boolean (STICKY_KEYS_ENABLE))
|
|
|
|
overview.toggle();
|
|
|
|
}));
|
2012-05-16 18:59:02 -04:00
|
|
|
|
2011-01-06 10:30:15 -05:00
|
|
|
// Provide the bus object for gnome-session to
|
|
|
|
// initiate logouts.
|
|
|
|
EndSessionDialog.init();
|
|
|
|
|
2013-02-28 10:43:39 -05:00
|
|
|
// We're ready for the session manager to move to the next phase
|
|
|
|
Meta.register_with_session();
|
|
|
|
|
2011-08-23 21:53:02 -04:00
|
|
|
_startDate = new Date();
|
2009-08-28 15:11:25 -04:00
|
|
|
|
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
|
|
|
|
2012-09-01 08:42:53 -04:00
|
|
|
ExtensionDownloader.init();
|
|
|
|
ExtensionSystem.init();
|
2013-02-13 22:45:43 -05:00
|
|
|
|
2013-03-06 18:01:12 -05:00
|
|
|
if (sessionMode.isGreeter && screenShield) {
|
|
|
|
layoutManager.connect('startup-prepared', function() {
|
|
|
|
screenShield.showDialog();
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2013-03-04 08:07:14 -05:00
|
|
|
layoutManager.connect('startup-complete', function() {
|
|
|
|
if (keybindingMode == Shell.KeyBindingMode.NONE) {
|
|
|
|
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
|
|
|
}
|
2013-01-17 14:39:54 -05:00
|
|
|
if (screenShield) {
|
|
|
|
screenShield.lockIfWasLocked();
|
|
|
|
}
|
2013-03-04 08:07:14 -05:00
|
|
|
});
|
2011-01-25 16:29:45 -05:00
|
|
|
}
|
|
|
|
|
2013-02-04 22:13:44 -05:00
|
|
|
function _loadDefaultStylesheet() {
|
2013-02-05 14:41:00 -05:00
|
|
|
if (!sessionMode.isPrimary)
|
2013-02-04 22:13:44 -05:00
|
|
|
return;
|
|
|
|
|
|
|
|
let stylesheet = global.datadir + '/theme/' + sessionMode.stylesheetName;
|
|
|
|
if (_defaultCssStylesheet == stylesheet)
|
|
|
|
return;
|
|
|
|
|
|
|
|
_defaultCssStylesheet = stylesheet;
|
|
|
|
loadTheme();
|
|
|
|
}
|
|
|
|
|
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-06-02 11:05:08 -04:00
|
|
|
let previousTheme = themeContext.get_theme();
|
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
|
|
|
|
2013-05-10 10:00:20 -04:00
|
|
|
let theme = new St.Theme ({ application_stylesheet: cssStylesheet,
|
|
|
|
default_stylesheet: _defaultCssStylesheet });
|
2011-06-02 11:05:08 -04:00
|
|
|
|
|
|
|
if (previousTheme) {
|
|
|
|
let customStylesheets = previousTheme.get_custom_stylesheets();
|
|
|
|
|
|
|
|
for (let i = 0; i < customStylesheets.length; i++)
|
|
|
|
theme.load_stylesheet(customStylesheets[i]);
|
|
|
|
}
|
|
|
|
|
2011-01-04 14:30:03 -05:00
|
|
|
themeContext.set_theme (theme);
|
|
|
|
}
|
|
|
|
|
2011-07-28 14:06:24 -04:00
|
|
|
/**
|
|
|
|
* notify:
|
|
|
|
* @msg: A message
|
|
|
|
* @details: Additional information
|
|
|
|
*/
|
|
|
|
function notify(msg, details) {
|
|
|
|
let source = new MessageTray.SystemNotificationSource();
|
|
|
|
messageTray.add(source);
|
|
|
|
let notification = new MessageTray.Notification(source, msg, details);
|
|
|
|
notification.setTransient(true);
|
|
|
|
source.notify(notification);
|
|
|
|
}
|
|
|
|
|
2011-03-15 18:31:16 -04:00
|
|
|
/**
|
|
|
|
* notifyError:
|
|
|
|
* @msg: An error message
|
|
|
|
* @details: Additional information
|
|
|
|
*
|
|
|
|
* See shell_global_notify_problem().
|
|
|
|
*/
|
|
|
|
function notifyError(msg, details) {
|
|
|
|
// Also print to stderr so it's logged somewhere
|
|
|
|
if (details)
|
2011-09-30 17:30:47 -04:00
|
|
|
log('error: ' + msg + ': ' + details);
|
2011-03-15 18:31:16 -04:00
|
|
|
else
|
2011-09-30 17:30:47 -04:00
|
|
|
log('error: ' + msg);
|
2011-03-15 18:31:16 -04:00
|
|
|
|
2011-07-28 14:06:24 -04:00
|
|
|
notify(msg, details);
|
2011-03-15 18:31:16 -04:00
|
|
|
}
|
|
|
|
|
2009-09-15 15:53:07 -04:00
|
|
|
function _findModal(actor) {
|
|
|
|
for (let i = 0; i < modalActorFocusStack.length; i++) {
|
2011-02-24 17:29:20 -05:00
|
|
|
if (modalActorFocusStack[i].actor == actor)
|
2009-09-15 15:53:07 -04:00
|
|
|
return i;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* pushModal:
|
|
|
|
* @actor: #ClutterActor which will be given keyboard focus
|
2012-08-10 11:54:39 -04:00
|
|
|
* @params: optional parameters
|
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
|
|
|
*
|
2012-08-10 11:54:39 -04:00
|
|
|
* @params may be used to provide the following parameters:
|
|
|
|
* - timestamp: used to associate the call with a specific user initiated
|
|
|
|
* event. If not provided then the value of
|
|
|
|
* global.get_current_time() is assumed.
|
2010-10-20 15:08:14 -04:00
|
|
|
*
|
2012-08-10 11:54:39 -04:00
|
|
|
* - options: Meta.ModalOptions flags to indicate that the pointer is
|
|
|
|
* already grabbed
|
2011-10-21 03:12:17 -04:00
|
|
|
*
|
2012-12-12 08:14:13 -05:00
|
|
|
* - keybindingMode: used to set the current Shell.KeyBindingMode to filter
|
2012-08-10 14:35:59 -04:00
|
|
|
* global keybindings; the default of NONE will filter
|
|
|
|
* out all keybindings
|
|
|
|
*
|
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
|
|
|
*/
|
2012-08-10 11:54:39 -04:00
|
|
|
function pushModal(actor, params) {
|
|
|
|
params = Params.parse(params, { timestamp: global.get_current_time(),
|
2012-08-10 14:35:59 -04:00
|
|
|
options: 0,
|
2012-12-12 08:14:13 -05:00
|
|
|
keybindingMode: Shell.KeyBindingMode.NONE });
|
2010-10-20 15:08:14 -04:00
|
|
|
|
2009-09-16 11:37:51 -04:00
|
|
|
if (modalCount == 0) {
|
2012-08-10 11:54:39 -04:00
|
|
|
if (!global.begin_modal(params.timestamp, params.options)) {
|
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;
|
|
|
|
}
|
2012-06-19 13:48:02 -04:00
|
|
|
Meta.disable_unredirect_for_screen(global.screen);
|
2009-09-16 11:37:51 -04:00
|
|
|
}
|
|
|
|
|
2009-09-15 15:53:07 -04:00
|
|
|
modalCount += 1;
|
2011-02-24 17:29:20 -05:00
|
|
|
let actorDestroyId = actor.connect('destroy', function() {
|
2009-09-15 15:53:07 -04:00
|
|
|
let index = _findModal(actor);
|
|
|
|
if (index >= 0)
|
2012-05-21 12:44:21 -04:00
|
|
|
popModal(actor);
|
2009-09-15 15:53:07 -04:00
|
|
|
});
|
2013-01-02 09:13:24 -05:00
|
|
|
|
|
|
|
let prevFocus = global.stage.get_key_focus();
|
|
|
|
let prevFocusDestroyId;
|
|
|
|
if (prevFocus != null) {
|
|
|
|
prevFocusDestroyId = prevFocus.connect('destroy', function() {
|
2009-09-15 15:53:07 -04:00
|
|
|
let index = _findModal(actor);
|
|
|
|
if (index >= 0)
|
2013-01-02 09:13:24 -05:00
|
|
|
modalActorFocusStack[index].prevFocus = null;
|
2009-09-15 15:53:07 -04:00
|
|
|
});
|
|
|
|
}
|
2011-02-24 17:29:20 -05:00
|
|
|
modalActorFocusStack.push({ actor: actor,
|
|
|
|
destroyId: actorDestroyId,
|
2013-01-02 09:13:24 -05:00
|
|
|
prevFocus: prevFocus,
|
|
|
|
prevFocusDestroyId: prevFocusDestroyId,
|
2012-08-10 14:35:59 -04:00
|
|
|
keybindingMode: keybindingMode });
|
2008-11-24 14:07:18 -05:00
|
|
|
|
2012-08-10 14:35:59 -04:00
|
|
|
keybindingMode = params.keybindingMode;
|
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
|
|
|
let focusIndex = _findModal(actor);
|
2011-02-24 17:29:20 -05:00
|
|
|
if (focusIndex < 0) {
|
|
|
|
global.stage.set_key_focus(null);
|
|
|
|
global.end_modal(timestamp);
|
2012-12-12 08:14:13 -05:00
|
|
|
keybindingMode = Shell.KeyBindingMode.NORMAL;
|
2011-02-24 17:29:20 -05:00
|
|
|
|
|
|
|
throw new Error('incorrect pop');
|
|
|
|
}
|
|
|
|
|
|
|
|
modalCount -= 1;
|
|
|
|
|
|
|
|
let record = modalActorFocusStack[focusIndex];
|
|
|
|
record.actor.disconnect(record.destroyId);
|
|
|
|
|
|
|
|
if (focusIndex == modalActorFocusStack.length - 1) {
|
2013-01-02 09:13:24 -05:00
|
|
|
if (record.prevFocus)
|
|
|
|
record.prevFocus.disconnect(record.prevFocusDestroyId);
|
2012-08-10 14:35:59 -04:00
|
|
|
keybindingMode = record.keybindingMode;
|
2013-01-02 09:13:24 -05:00
|
|
|
global.stage.set_key_focus(record.prevFocus);
|
2011-02-24 17:29:20 -05:00
|
|
|
} else {
|
2013-01-02 09:19:41 -05:00
|
|
|
// If we have:
|
|
|
|
// global.stage.set_focus(a);
|
|
|
|
// Main.pushModal(b);
|
|
|
|
// Main.pushModal(c);
|
|
|
|
// Main.pushModal(d);
|
|
|
|
//
|
|
|
|
// then we have the stack:
|
|
|
|
// [{ prevFocus: a, actor: b },
|
|
|
|
// { prevFocus: b, actor: c },
|
|
|
|
// { prevFocus: c, actor: d }]
|
|
|
|
//
|
|
|
|
// When actor c is destroyed/popped, if we only simply remove the
|
|
|
|
// record, then the focus stack will be [a, c], rather than the correct
|
|
|
|
// [a, b]. Shift the focus stack up before removing the record to ensure
|
|
|
|
// that we get the correct result.
|
2011-02-24 17:29:20 -05:00
|
|
|
let t = modalActorFocusStack[modalActorFocusStack.length - 1];
|
2013-01-02 09:13:24 -05:00
|
|
|
if (t.prevFocus)
|
|
|
|
t.prevFocus.disconnect(t.prevFocusDestroyId);
|
2011-02-24 17:29:20 -05:00
|
|
|
// Remove from the middle, shift the focus chain up
|
|
|
|
for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
|
2013-01-02 09:13:24 -05:00
|
|
|
modalActorFocusStack[i].prevFocus = modalActorFocusStack[i - 1].prevFocus;
|
|
|
|
modalActorFocusStack[i].prevFocusDestroyId = modalActorFocusStack[i - 1].prevFocusDestroyId;
|
2012-08-10 14:35:59 -04:00
|
|
|
modalActorFocusStack[i].keybindingMode = modalActorFocusStack[i - 1].keybindingMode;
|
2009-09-15 15:53:07 -04:00
|
|
|
}
|
|
|
|
}
|
2011-02-24 17:29:20 -05:00
|
|
|
modalActorFocusStack.splice(focusIndex, 1);
|
|
|
|
|
2009-09-15 15:53:07 -04:00
|
|
|
if (modalCount > 0)
|
|
|
|
return;
|
|
|
|
|
2010-10-20 15:08:14 -04:00
|
|
|
global.end_modal(timestamp);
|
2012-06-19 13:48:02 -04:00
|
|
|
Meta.enable_unredirect_for_screen(global.screen);
|
2012-12-12 08:14:13 -05:00
|
|
|
keybindingMode = Shell.KeyBindingMode.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();
|
|
|
|
}
|
|
|
|
return lookingGlass;
|
|
|
|
}
|
|
|
|
|
2012-09-01 08:42:53 -04:00
|
|
|
function openRunDialog() {
|
2009-09-14 15:08:20 -04:00
|
|
|
if (runDialog == null) {
|
|
|
|
runDialog = new RunDialog.RunDialog();
|
|
|
|
}
|
2012-09-01 08:42:53 -04:00
|
|
|
runDialog.open();
|
2009-09-14 15:08:20 -04:00
|
|
|
}
|
|
|
|
|
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) {
|
2012-05-09 21:37:42 -04:00
|
|
|
let message = 'Invalid work id %d'.format(workId);
|
|
|
|
logError(new Error(message), message);
|
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;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|