~ubuntu-branches/ubuntu/trusty/gnome-shell/trusty-proposed

« back to all changes in this revision

Viewing changes to .pc/29-Cope-with-clutter-being-built-with-both-new-and-old-.patch/js/ui/main.js

  • Committer: Package Import Robot
  • Author(s): Emilio Pozuelo Monfort, Sjoerd Simons, Emilio Pozuelo Monfort
  • Date: 2013-03-20 22:40:26 UTC
  • mfrom: (1.4.1) (67 raring-proposed)
  • mto: (18.1.33 sid)
  • mto: This revision was merged to the branch mainline in revision 81.
  • Revision ID: package-import@ubuntu.com-20130320224026-zgrrbbiezhjgc5dc
Tags: 3.7.92-1
[ Sjoerd Simons ]
* Sync from Ubuntu
* d/p/ubuntu-lightdm-user-switching.patch:
  d/p/ubuntu_lock_on_suspend.patch:
  + Dropped, Ubuntu specifc
* d/p/14_make-GLX-optional.patch: Fixed upstream
* debian/control.in: Depend on e-d-s >= 3.7.90 to fix build failures due to
  deprecated structures in headers.
* debian/patches/40_change-pam-name-to-match-gdm.patch
  + Added. Change the pam service name to match the one used by gdm in debian

[ Emilio Pozuelo Monfort ]
* New upstream release.
  + debian/control.in:
    - Update build dependencies.
    - Temporarily add an explicit build-depend on network-manager-dev
      until libnm-glib-dev gets a versioned dependency.
  + debian/patches/git_fix_too_short_apps_view.patch:
    - Removed, included upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
2
 
 
3
 
const Clutter = imports.gi.Clutter;
4
 
const Gdk = imports.gi.Gdk;
5
 
const Gio = imports.gi.Gio;
6
 
const GLib = imports.gi.GLib;
7
 
const Lang = imports.lang;
8
 
const Mainloop = imports.mainloop;
9
 
const Meta = imports.gi.Meta;
10
 
const Shell = imports.gi.Shell;
11
 
const St = imports.gi.St;
12
 
 
13
 
const Components = imports.ui.components;
14
 
const CtrlAltTab = imports.ui.ctrlAltTab;
15
 
const EndSessionDialog = imports.ui.endSessionDialog;
16
 
const Environment = imports.ui.environment;
17
 
const ExtensionSystem = imports.ui.extensionSystem;
18
 
const ExtensionDownloader = imports.ui.extensionDownloader;
19
 
const Keyboard = imports.ui.keyboard;
20
 
const MessageTray = imports.ui.messageTray;
21
 
const Overview = imports.ui.overview;
22
 
const Panel = imports.ui.panel;
23
 
const RunDialog = imports.ui.runDialog;
24
 
const Layout = imports.ui.layout;
25
 
const LookingGlass = imports.ui.lookingGlass;
26
 
const NotificationDaemon = imports.ui.notificationDaemon;
27
 
const WindowAttentionHandler = imports.ui.windowAttentionHandler;
28
 
const ScreenShield = imports.ui.screenShield;
29
 
const Scripting = imports.ui.scripting;
30
 
const SessionMode = imports.ui.sessionMode;
31
 
const ShellDBus = imports.ui.shellDBus;
32
 
const ShellMountOperation = imports.ui.shellMountOperation;
33
 
const UnlockDialog = imports.ui.unlockDialog;
34
 
const WindowManager = imports.ui.windowManager;
35
 
const Magnifier = imports.ui.magnifier;
36
 
const XdndHandler = imports.ui.xdndHandler;
37
 
const Util = imports.misc.util;
38
 
 
39
 
const OVERRIDES_SCHEMA = 'org.gnome.shell.overrides';
40
 
const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
41
 
 
42
 
let componentManager = null;
43
 
let panel = null;
44
 
let overview = null;
45
 
let runDialog = null;
46
 
let lookingGlass = null;
47
 
let wm = null;
48
 
let messageTray = null;
49
 
let screenShield = null;
50
 
let notificationDaemon = null;
51
 
let windowAttentionHandler = null;
52
 
let ctrlAltTabManager = null;
53
 
let sessionMode = null;
54
 
let shellDBusService = null;
55
 
let shellMountOpDBusService = null;
56
 
let screenSaverDBus = null;
57
 
let modalCount = 0;
58
 
let modalActorFocusStack = [];
59
 
let uiGroup = null;
60
 
let magnifier = null;
61
 
let xdndHandler = null;
62
 
let keyboard = null;
63
 
let layoutManager = null;
64
 
let _startDate;
65
 
let _defaultCssStylesheet = null;
66
 
let _cssStylesheet = null;
67
 
let _overridesSettings = null;
68
 
 
69
 
let background = null;
70
 
 
71
 
function _sessionUpdated() {
72
 
    Meta.keybindings_set_custom_handler('panel-run-dialog', sessionMode.hasRunDialog ? openRunDialog : null);
73
 
    if (sessionMode.isGreeter)
74
 
        screenShield.showDialog();
75
 
}
76
 
 
77
 
function start() {
78
 
    // These are here so we don't break compatibility.
79
 
    global.logError = window.log;
80
 
    global.log = window.log;
81
 
 
82
 
    // Chain up async errors reported from C
83
 
    global.connect('notify-error', function (global, msg, detail) { notifyError(msg, detail); });
84
 
 
85
 
    Gio.DesktopAppInfo.set_desktop_env('GNOME');
86
 
 
87
 
    sessionMode = new SessionMode.SessionMode();
88
 
    shellDBusService = new ShellDBus.GnomeShell();
89
 
    shellMountOpDBusService = new ShellMountOperation.GnomeShellMountOpHandler();
90
 
 
91
 
    // Ensure ShellWindowTracker and ShellAppUsage are initialized; this will
92
 
    // also initialize ShellAppSystem first.  ShellAppSystem
93
 
    // needs to load all the .desktop files, and ShellWindowTracker
94
 
    // will use those to associate with windows.  Right now
95
 
    // the Monitor doesn't listen for installed app changes
96
 
    // and recalculate application associations, so to avoid
97
 
    // races for now we initialize it here.  It's better to
98
 
    // be predictable anyways.
99
 
    let tracker = Shell.WindowTracker.get_default();
100
 
    Shell.AppUsage.get_default();
101
 
 
102
 
    tracker.connect('startup-sequence-changed', _queueCheckWorkspaces);
103
 
 
104
 
    // The stage is always covered so Clutter doesn't need to clear it; however
105
 
    // the color is used as the default contents for the Mutter root background
106
 
    // actor so set it anyways.
107
 
    global.stage.color = DEFAULT_BACKGROUND_COLOR;
108
 
    global.stage.no_clear_hint = true;
109
 
 
110
 
    _defaultCssStylesheet = global.datadir + '/theme/gnome-shell.css';
111
 
    loadTheme();
112
 
 
113
 
    // Set up stage hierarchy to group all UI actors under one container.
114
 
    uiGroup = new Shell.GenericContainer({ name: 'uiGroup' });
115
 
    uiGroup.connect('allocate',
116
 
                    function (actor, box, flags) {
117
 
                        let children = uiGroup.get_children();
118
 
                        for (let i = 0; i < children.length; i++)
119
 
                            children[i].allocate_preferred_size(flags);
120
 
                    });
121
 
    uiGroup.connect('get-preferred-width',
122
 
                    function(actor, forHeight, alloc) {
123
 
                        let width = global.stage.width;
124
 
                        [alloc.min_size, alloc.natural_size] = [width, width];
125
 
                    });
126
 
    uiGroup.connect('get-preferred-height',
127
 
                    function(actor, forWidth, alloc) {
128
 
                        let height = global.stage.height;
129
 
                        [alloc.min_size, alloc.natural_size] = [height, height];
130
 
                    });
131
 
    global.window_group.reparent(uiGroup);
132
 
    global.overlay_group.reparent(uiGroup);
133
 
    global.stage.add_actor(uiGroup);
134
 
 
135
 
    layoutManager = new Layout.LayoutManager();
136
 
    xdndHandler = new XdndHandler.XdndHandler();
137
 
    ctrlAltTabManager = new CtrlAltTab.CtrlAltTabManager();
138
 
    overview = new Overview.Overview();
139
 
    magnifier = new Magnifier.Magnifier();
140
 
    if (UnlockDialog.isSupported())
141
 
        screenShield = new ScreenShield.ScreenShield();
142
 
    else
143
 
        screenShield = new ScreenShield.ScreenShieldFallback();
144
 
    panel = new Panel.Panel();
145
 
    wm = new WindowManager.WindowManager();
146
 
    messageTray = new MessageTray.MessageTray();
147
 
    keyboard = new Keyboard.Keyboard();
148
 
    notificationDaemon = new NotificationDaemon.NotificationDaemon();
149
 
    windowAttentionHandler = new WindowAttentionHandler.WindowAttentionHandler();
150
 
    componentManager = new Components.ComponentManager();
151
 
 
152
 
    layoutManager.init();
153
 
    keyboard.init();
154
 
    overview.init();
155
 
 
156
 
    global.screen.override_workspace_layout(Meta.ScreenCorner.TOPLEFT,
157
 
                                            false, -1, 1);
158
 
    Meta.keybindings_set_custom_handler('panel-main-menu', Lang.bind(overview, overview.toggle));
159
 
    global.display.connect('overlay-key', Lang.bind(overview, overview.toggle));
160
 
 
161
 
    sessionMode.connect('update', _sessionUpdated);
162
 
    _sessionUpdated();
163
 
 
164
 
    // Provide the bus object for gnome-session to
165
 
    // initiate logouts.
166
 
    EndSessionDialog.init();
167
 
 
168
 
    _startDate = new Date();
169
 
 
170
 
    global.stage.connect('captured-event', _globalKeyPressHandler);
171
 
 
172
 
    log('GNOME Shell started at ' + _startDate);
173
 
 
174
 
    let perfModuleName = GLib.getenv("SHELL_PERF_MODULE");
175
 
    if (perfModuleName) {
176
 
        let perfOutput = GLib.getenv("SHELL_PERF_OUTPUT");
177
 
        let module = eval('imports.perf.' + perfModuleName + ';');
178
 
        Scripting.runPerfScript(module, perfOutput);
179
 
    }
180
 
 
181
 
    _overridesSettings = new Gio.Settings({ schema: OVERRIDES_SCHEMA });
182
 
    _overridesSettings.connect('changed::dynamic-workspaces', _queueCheckWorkspaces);
183
 
 
184
 
    global.screen.connect('notify::n-workspaces', _nWorkspacesChanged);
185
 
 
186
 
    global.screen.connect('window-entered-monitor', _windowEnteredMonitor);
187
 
    global.screen.connect('window-left-monitor', _windowLeftMonitor);
188
 
    global.screen.connect('restacked', _windowsRestacked);
189
 
 
190
 
    _nWorkspacesChanged();
191
 
 
192
 
    ExtensionDownloader.init();
193
 
    ExtensionSystem.init();
194
 
}
195
 
 
196
 
let _workspaces = [];
197
 
let _checkWorkspacesId = 0;
198
 
 
199
 
/*
200
 
 * When the last window closed on a workspace is a dialog or splash
201
 
 * screen, we assume that it might be an initial window shown before
202
 
 * the main window of an application, and give the app a grace period
203
 
 * where it can map another window before we remove the workspace.
204
 
 */
205
 
const LAST_WINDOW_GRACE_TIME = 1000;
206
 
 
207
 
function _checkWorkspaces() {
208
 
    let i;
209
 
    let emptyWorkspaces = [];
210
 
 
211
 
    if (!Meta.prefs_get_dynamic_workspaces()) {
212
 
        _checkWorkspacesId = 0;
213
 
        return false;
214
 
    }
215
 
 
216
 
    for (i = 0; i < _workspaces.length; i++) {
217
 
        let lastRemoved = _workspaces[i]._lastRemovedWindow;
218
 
        if ((lastRemoved &&
219
 
             (lastRemoved.get_window_type() == Meta.WindowType.SPLASHSCREEN ||
220
 
              lastRemoved.get_window_type() == Meta.WindowType.DIALOG ||
221
 
              lastRemoved.get_window_type() == Meta.WindowType.MODAL_DIALOG)) ||
222
 
            _workspaces[i]._keepAliveId)
223
 
                emptyWorkspaces[i] = false;
224
 
        else
225
 
            emptyWorkspaces[i] = true;
226
 
    }
227
 
 
228
 
    let sequences = Shell.WindowTracker.get_default().get_startup_sequences();
229
 
    for (i = 0; i < sequences.length; i++) {
230
 
        let index = sequences[i].get_workspace();
231
 
        if (index >= 0 && index <= global.screen.n_workspaces)
232
 
            emptyWorkspaces[index] = false;
233
 
    }
234
 
 
235
 
    let windows = global.get_window_actors();
236
 
    for (i = 0; i < windows.length; i++) {
237
 
        let win = windows[i];
238
 
 
239
 
        if (win.get_meta_window().is_on_all_workspaces())
240
 
            continue;
241
 
 
242
 
        let workspaceIndex = win.get_workspace();
243
 
        emptyWorkspaces[workspaceIndex] = false;
244
 
    }
245
 
 
246
 
    // If we don't have an empty workspace at the end, add one
247
 
    if (!emptyWorkspaces[emptyWorkspaces.length -1]) {
248
 
        global.screen.append_new_workspace(false, global.get_current_time());
249
 
        emptyWorkspaces.push(false);
250
 
    }
251
 
 
252
 
    let activeWorkspaceIndex = global.screen.get_active_workspace_index();
253
 
    let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] &&
254
 
                                    activeWorkspaceIndex < emptyWorkspaces.length - 1);
255
 
    // Don't enter the overview when removing multiple empty workspaces at startup
256
 
    let showOverview  = (removingCurrentWorkspace &&
257
 
                         !emptyWorkspaces.every(function(x) { return x; }));
258
 
 
259
 
    if (removingCurrentWorkspace) {
260
 
        // "Merge" the empty workspace we are removing with the one at the end
261
 
        wm.blockAnimations();
262
 
    }
263
 
 
264
 
    // Delete other empty workspaces; do it from the end to avoid index changes
265
 
    for (i = emptyWorkspaces.length - 2; i >= 0; i--) {
266
 
        if (emptyWorkspaces[i])
267
 
            global.screen.remove_workspace(_workspaces[i], global.get_current_time());
268
 
    }
269
 
 
270
 
    if (removingCurrentWorkspace) {
271
 
        global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time());
272
 
        wm.unblockAnimations();
273
 
 
274
 
        if (!overview.visible && showOverview)
275
 
            overview.show();
276
 
    }
277
 
 
278
 
    _checkWorkspacesId = 0;
279
 
    return false;
280
 
}
281
 
 
282
 
function keepWorkspaceAlive(workspace, duration) {
283
 
    if (workspace._keepAliveId)
284
 
        Mainloop.source_remove(workspace._keepAliveId);
285
 
 
286
 
    workspace._keepAliveId = Mainloop.timeout_add(duration, function() {
287
 
        workspace._keepAliveId = 0;
288
 
        _queueCheckWorkspaces();
289
 
        return false;
290
 
    });
291
 
}
292
 
 
293
 
function _windowRemoved(workspace, window) {
294
 
    workspace._lastRemovedWindow = window;
295
 
    _queueCheckWorkspaces();
296
 
    Mainloop.timeout_add(LAST_WINDOW_GRACE_TIME, function() {
297
 
        if (workspace._lastRemovedWindow == window) {
298
 
            workspace._lastRemovedWindow = null;
299
 
            _queueCheckWorkspaces();
300
 
        }
301
 
        return false;
302
 
    });
303
 
}
304
 
 
305
 
function _windowLeftMonitor(metaScreen, monitorIndex, metaWin) {
306
 
    // If the window left the primary monitor, that
307
 
    // might make that workspace empty
308
 
    if (monitorIndex == layoutManager.primaryIndex)
309
 
        _queueCheckWorkspaces();
310
 
}
311
 
 
312
 
function _windowEnteredMonitor(metaScreen, monitorIndex, metaWin) {
313
 
    // If the window entered the primary monitor, that
314
 
    // might make that workspace non-empty
315
 
    if (monitorIndex == layoutManager.primaryIndex)
316
 
        _queueCheckWorkspaces();
317
 
}
318
 
 
319
 
function _windowsRestacked() {
320
 
    // Figure out where the pointer is in case we lost track of
321
 
    // it during a grab. (In particular, if a trayicon popup menu
322
 
    // is dismissed, see if we need to close the message tray.)
323
 
    global.sync_pointer();
324
 
}
325
 
 
326
 
function _queueCheckWorkspaces() {
327
 
    if (_checkWorkspacesId == 0)
328
 
        _checkWorkspacesId = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, _checkWorkspaces);
329
 
}
330
 
 
331
 
function _nWorkspacesChanged() {
332
 
    let oldNumWorkspaces = _workspaces.length;
333
 
    let newNumWorkspaces = global.screen.n_workspaces;
334
 
 
335
 
    if (oldNumWorkspaces == newNumWorkspaces)
336
 
        return false;
337
 
 
338
 
    let lostWorkspaces = [];
339
 
    if (newNumWorkspaces > oldNumWorkspaces) {
340
 
        let w;
341
 
 
342
 
        // Assume workspaces are only added at the end
343
 
        for (w = oldNumWorkspaces; w < newNumWorkspaces; w++)
344
 
            _workspaces[w] = global.screen.get_workspace_by_index(w);
345
 
 
346
 
        for (w = oldNumWorkspaces; w < newNumWorkspaces; w++) {
347
 
            let workspace = _workspaces[w];
348
 
            workspace._windowAddedId = workspace.connect('window-added', _queueCheckWorkspaces);
349
 
            workspace._windowRemovedId = workspace.connect('window-removed', _windowRemoved);
350
 
        }
351
 
 
352
 
    } else {
353
 
        // Assume workspaces are only removed sequentially
354
 
        // (e.g. 2,3,4 - not 2,4,7)
355
 
        let removedIndex;
356
 
        let removedNum = oldNumWorkspaces - newNumWorkspaces;
357
 
        for (let w = 0; w < oldNumWorkspaces; w++) {
358
 
            let workspace = global.screen.get_workspace_by_index(w);
359
 
            if (_workspaces[w] != workspace) {
360
 
                removedIndex = w;
361
 
                break;
362
 
            }
363
 
        }
364
 
 
365
 
        let lostWorkspaces = _workspaces.splice(removedIndex, removedNum);
366
 
        lostWorkspaces.forEach(function(workspace) {
367
 
                                   workspace.disconnect(workspace._windowAddedId);
368
 
                                   workspace.disconnect(workspace._windowRemovedId);
369
 
                               });
370
 
    }
371
 
 
372
 
    _queueCheckWorkspaces();
373
 
 
374
 
    return false;
375
 
}
376
 
 
377
 
/**
378
 
 * getThemeStylesheet:
379
 
 *
380
 
 * Get the theme CSS file that the shell will load
381
 
 *
382
 
 * Returns: A file path that contains the theme CSS,
383
 
 *          null if using the default
384
 
 */
385
 
function getThemeStylesheet()
386
 
{
387
 
    return _cssStylesheet;
388
 
}
389
 
 
390
 
/**
391
 
 * setThemeStylesheet:
392
 
 * @cssStylesheet: A file path that contains the theme CSS,
393
 
 *                  set it to null to use the default
394
 
 *
395
 
 * Set the theme CSS file that the shell will load
396
 
 */
397
 
function setThemeStylesheet(cssStylesheet)
398
 
{
399
 
    _cssStylesheet = cssStylesheet;
400
 
}
401
 
 
402
 
/**
403
 
 * loadTheme:
404
 
 *
405
 
 * Reloads the theme CSS file
406
 
 */
407
 
function loadTheme() {
408
 
    let themeContext = St.ThemeContext.get_for_stage (global.stage);
409
 
    let previousTheme = themeContext.get_theme();
410
 
 
411
 
    let cssStylesheet = _defaultCssStylesheet;
412
 
    if (_cssStylesheet != null)
413
 
        cssStylesheet = _cssStylesheet;
414
 
 
415
 
    let theme = new St.Theme ({ application_stylesheet: cssStylesheet });
416
 
 
417
 
    if (previousTheme) {
418
 
        let customStylesheets = previousTheme.get_custom_stylesheets();
419
 
 
420
 
        for (let i = 0; i < customStylesheets.length; i++)
421
 
            theme.load_stylesheet(customStylesheets[i]);
422
 
    }
423
 
 
424
 
    themeContext.set_theme (theme);
425
 
}
426
 
 
427
 
/**
428
 
 * notify:
429
 
 * @msg: A message
430
 
 * @details: Additional information
431
 
 */
432
 
function notify(msg, details) {
433
 
    let source = new MessageTray.SystemNotificationSource();
434
 
    messageTray.add(source);
435
 
    let notification = new MessageTray.Notification(source, msg, details);
436
 
    notification.setTransient(true);
437
 
    source.notify(notification);
438
 
}
439
 
 
440
 
/**
441
 
 * notifyError:
442
 
 * @msg: An error message
443
 
 * @details: Additional information
444
 
 *
445
 
 * See shell_global_notify_problem().
446
 
 */
447
 
function notifyError(msg, details) {
448
 
    // Also print to stderr so it's logged somewhere
449
 
    if (details)
450
 
        log('error: ' + msg + ': ' + details);
451
 
    else
452
 
        log('error: ' + msg);
453
 
 
454
 
    notify(msg, details);
455
 
}
456
 
 
457
 
function isWindowActorDisplayedOnWorkspace(win, workspaceIndex) {
458
 
    return win.get_workspace() == workspaceIndex ||
459
 
        (win.get_meta_window() && win.get_meta_window().is_on_all_workspaces());
460
 
}
461
 
 
462
 
function getWindowActorsForWorkspace(workspaceIndex) {
463
 
    return global.get_window_actors().filter(function (win) {
464
 
        return isWindowActorDisplayedOnWorkspace(win, workspaceIndex);
465
 
    });
466
 
}
467
 
 
468
 
// This function encapsulates hacks to make certain global keybindings
469
 
// work even when we are in one of our modes where global keybindings
470
 
// are disabled with a global grab. (When there is a global grab, then
471
 
// all key events will be delivered to the stage, so ::captured-event
472
 
// on the stage can be used for global keybindings.)
473
 
function _globalKeyPressHandler(actor, event) {
474
 
    if (modalCount == 0)
475
 
        return false;
476
 
    if (event.type() != Clutter.EventType.KEY_PRESS)
477
 
        return false;
478
 
 
479
 
    if (!sessionMode.allowKeybindingsWhenModal) {
480
 
        if (modalCount > (overview.visible ? 1 : 0))
481
 
            return false;
482
 
    }
483
 
 
484
 
    let symbol = event.get_key_symbol();
485
 
    let keyCode = event.get_key_code();
486
 
    let ignoredModifiers = global.display.get_ignored_modifier_mask();
487
 
    let modifierState = event.get_state() & ~ignoredModifiers;
488
 
 
489
 
    // This relies on the fact that Clutter.ModifierType is the same as Gdk.ModifierType
490
 
    let action = global.display.get_keybinding_action(keyCode, modifierState);
491
 
 
492
 
    if (action == Meta.KeyBindingAction.SWITCH_PANELS) {
493
 
        ctrlAltTabManager.popup(modifierState & Clutter.ModifierType.SHIFT_MASK,
494
 
                                modifierState);
495
 
        return true;
496
 
    }
497
 
 
498
 
    switch (action) {
499
 
        // left/right would effectively act as synonyms for up/down if we enabled them;
500
 
        // but that could be considered confusing; we also disable them in the main view.
501
 
        //
502
 
        // case Meta.KeyBindingAction.WORKSPACE_LEFT:
503
 
        //  if (!sessionMode.hasWorkspaces)
504
 
        //      return false;
505
 
        //
506
 
        //     wm.actionMoveWorkspaceLeft();
507
 
        //     return true;
508
 
        // case Meta.KeyBindingAction.WORKSPACE_RIGHT:
509
 
        //  if (!sessionMode.hasWorkspaces)
510
 
        //      return false;
511
 
        //
512
 
        //     wm.actionMoveWorkspaceRight();
513
 
        //     return true;
514
 
        case Meta.KeyBindingAction.WORKSPACE_UP:
515
 
            if (!sessionMode.hasWorkspaces)
516
 
                return false;
517
 
 
518
 
            wm.actionMoveWorkspace(Meta.MotionDirection.UP);
519
 
            return true;
520
 
        case Meta.KeyBindingAction.WORKSPACE_DOWN:
521
 
            if (!sessionMode.hasWorkspaces)
522
 
                return false;
523
 
 
524
 
            wm.actionMoveWorkspace(Meta.MotionDirection.DOWN);
525
 
            return true;
526
 
        case Meta.KeyBindingAction.PANEL_RUN_DIALOG:
527
 
        case Meta.KeyBindingAction.COMMAND_2:
528
 
            if (!sessionMode.hasRunDialog)
529
 
                return false;
530
 
 
531
 
            openRunDialog();
532
 
            return true;
533
 
        case Meta.KeyBindingAction.PANEL_MAIN_MENU:
534
 
        case Meta.KeyBindingAction.OVERLAY_KEY:
535
 
            overview.hide();
536
 
            return true;
537
 
    }
538
 
 
539
 
    return false;
540
 
}
541
 
 
542
 
function _findModal(actor) {
543
 
    for (let i = 0; i < modalActorFocusStack.length; i++) {
544
 
        if (modalActorFocusStack[i].actor == actor)
545
 
            return i;
546
 
    }
547
 
    return -1;
548
 
}
549
 
 
550
 
function isInModalStack(actor) {
551
 
    return _findModal(actor) != -1;
552
 
}
553
 
 
554
 
/**
555
 
 * pushModal:
556
 
 * @actor: #ClutterActor which will be given keyboard focus
557
 
 * @timestamp: optional timestamp
558
 
 *
559
 
 * Ensure we are in a mode where all keyboard and mouse input goes to
560
 
 * the stage, and focus @actor. Multiple calls to this function act in
561
 
 * a stacking fashion; the effect will be undone when an equal number
562
 
 * of popModal() invocations have been made.
563
 
 *
564
 
 * Next, record the current Clutter keyboard focus on a stack. If the
565
 
 * modal stack returns to this actor, reset the focus to the actor
566
 
 * which was focused at the time pushModal() was invoked.
567
 
 *
568
 
 * @timestamp is optionally used to associate the call with a specific user
569
 
 * initiated event.  If not provided then the value of
570
 
 * global.get_current_time() is assumed.
571
 
 *
572
 
 * @options: optional Meta.ModalOptions flags to indicate that the
573
 
 *           pointer is alrady grabbed
574
 
 *
575
 
 * Returns: true iff we successfully acquired a grab or already had one
576
 
 */
577
 
function pushModal(actor, timestamp, options) {
578
 
    if (timestamp == undefined)
579
 
        timestamp = global.get_current_time();
580
 
 
581
 
    if (modalCount == 0) {
582
 
        if (!global.begin_modal(timestamp, options ? options : 0)) {
583
 
            log('pushModal: invocation of begin_modal failed');
584
 
            return false;
585
 
        }
586
 
        Meta.disable_unredirect_for_screen(global.screen);
587
 
    }
588
 
 
589
 
    global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
590
 
 
591
 
    modalCount += 1;
592
 
    let actorDestroyId = actor.connect('destroy', function() {
593
 
        let index = _findModal(actor);
594
 
        if (index >= 0)
595
 
            popModal(actor);
596
 
    });
597
 
    let curFocus = global.stage.get_key_focus();
598
 
    let curFocusDestroyId;
599
 
    if (curFocus != null) {
600
 
        curFocusDestroyId = curFocus.connect('destroy', function() {
601
 
            let index = _findModal(actor);
602
 
            if (index >= 0)
603
 
                modalActorFocusStack[index].actor = null;
604
 
        });
605
 
    }
606
 
    modalActorFocusStack.push({ actor: actor,
607
 
                                focus: curFocus,
608
 
                                destroyId: actorDestroyId,
609
 
                                focusDestroyId: curFocusDestroyId });
610
 
 
611
 
    global.stage.set_key_focus(actor);
612
 
    return true;
613
 
}
614
 
 
615
 
/**
616
 
 * popModal:
617
 
 * @actor: #ClutterActor passed to original invocation of pushModal().
618
 
 * @timestamp: optional timestamp
619
 
 *
620
 
 * Reverse the effect of pushModal().  If this invocation is undoing
621
 
 * the topmost invocation, then the focus will be restored to the
622
 
 * previous focus at the time when pushModal() was invoked.
623
 
 *
624
 
 * @timestamp is optionally used to associate the call with a specific user
625
 
 * initiated event.  If not provided then the value of
626
 
 * global.get_current_time() is assumed.
627
 
 */
628
 
function popModal(actor, timestamp) {
629
 
    if (timestamp == undefined)
630
 
        timestamp = global.get_current_time();
631
 
 
632
 
    let focusIndex = _findModal(actor);
633
 
    if (focusIndex < 0) {
634
 
        global.stage.set_key_focus(null);
635
 
        global.end_modal(timestamp);
636
 
        global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
637
 
 
638
 
        throw new Error('incorrect pop');
639
 
    }
640
 
 
641
 
    modalCount -= 1;
642
 
 
643
 
    let record = modalActorFocusStack[focusIndex];
644
 
    record.actor.disconnect(record.destroyId);
645
 
 
646
 
    if (focusIndex == modalActorFocusStack.length - 1) {
647
 
        if (record.focus)
648
 
            record.focus.disconnect(record.focusDestroyId);
649
 
        global.stage.set_key_focus(record.focus);
650
 
    } else {
651
 
        let t = modalActorFocusStack[modalActorFocusStack.length - 1];
652
 
        if (t.focus)
653
 
            t.focus.disconnect(t.focusDestroyId);
654
 
        // Remove from the middle, shift the focus chain up
655
 
        for (let i = modalActorFocusStack.length - 1; i > focusIndex; i--) {
656
 
            modalActorFocusStack[i].focus = modalActorFocusStack[i - 1].focus;
657
 
            modalActorFocusStack[i].focusDestroyId = modalActorFocusStack[i - 1].focusDestroyId;
658
 
        }
659
 
    }
660
 
    modalActorFocusStack.splice(focusIndex, 1);
661
 
 
662
 
    if (modalCount > 0)
663
 
        return;
664
 
 
665
 
    global.end_modal(timestamp);
666
 
    global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
667
 
    Meta.enable_unredirect_for_screen(global.screen);
668
 
}
669
 
 
670
 
function createLookingGlass() {
671
 
    if (lookingGlass == null) {
672
 
        lookingGlass = new LookingGlass.LookingGlass();
673
 
    }
674
 
    return lookingGlass;
675
 
}
676
 
 
677
 
function openRunDialog() {
678
 
    if (runDialog == null) {
679
 
        runDialog = new RunDialog.RunDialog();
680
 
    }
681
 
    runDialog.open();
682
 
}
683
 
 
684
 
/**
685
 
 * activateWindow:
686
 
 * @window: the Meta.Window to activate
687
 
 * @time: (optional) current event time
688
 
 * @workspaceNum: (optional) window's workspace number
689
 
 *
690
 
 * Activates @window, switching to its workspace first if necessary,
691
 
 * and switching out of the overview if it's currently active
692
 
 */
693
 
function activateWindow(window, time, workspaceNum) {
694
 
    let activeWorkspaceNum = global.screen.get_active_workspace_index();
695
 
    let windowWorkspaceNum = (workspaceNum !== undefined) ? workspaceNum : window.get_workspace().index();
696
 
 
697
 
    if (!time)
698
 
        time = global.get_current_time();
699
 
 
700
 
    if (windowWorkspaceNum != activeWorkspaceNum) {
701
 
        let workspace = global.screen.get_workspace_by_index(windowWorkspaceNum);
702
 
        workspace.activate_with_focus(window, time);
703
 
    } else {
704
 
        window.activate(time);
705
 
    }
706
 
 
707
 
    overview.hide();
708
 
}
709
 
 
710
 
// TODO - replace this timeout with some system to guess when the user might
711
 
// be e.g. just reading the screen and not likely to interact.
712
 
const DEFERRED_TIMEOUT_SECONDS = 20;
713
 
var _deferredWorkData = {};
714
 
// Work scheduled for some point in the future
715
 
var _deferredWorkQueue = [];
716
 
// Work we need to process before the next redraw
717
 
var _beforeRedrawQueue = [];
718
 
// Counter to assign work ids
719
 
var _deferredWorkSequence = 0;
720
 
var _deferredTimeoutId = 0;
721
 
 
722
 
function _runDeferredWork(workId) {
723
 
    if (!_deferredWorkData[workId])
724
 
        return;
725
 
    let index = _deferredWorkQueue.indexOf(workId);
726
 
    if (index < 0)
727
 
        return;
728
 
 
729
 
    _deferredWorkQueue.splice(index, 1);
730
 
    _deferredWorkData[workId].callback();
731
 
    if (_deferredWorkQueue.length == 0 && _deferredTimeoutId > 0) {
732
 
        Mainloop.source_remove(_deferredTimeoutId);
733
 
        _deferredTimeoutId = 0;
734
 
    }
735
 
}
736
 
 
737
 
function _runAllDeferredWork() {
738
 
    while (_deferredWorkQueue.length > 0)
739
 
        _runDeferredWork(_deferredWorkQueue[0]);
740
 
}
741
 
 
742
 
function _runBeforeRedrawQueue() {
743
 
    for (let i = 0; i < _beforeRedrawQueue.length; i++) {
744
 
        let workId = _beforeRedrawQueue[i];
745
 
        _runDeferredWork(workId);
746
 
    }
747
 
    _beforeRedrawQueue = [];
748
 
}
749
 
 
750
 
function _queueBeforeRedraw(workId) {
751
 
    _beforeRedrawQueue.push(workId);
752
 
    if (_beforeRedrawQueue.length == 1) {
753
 
        Meta.later_add(Meta.LaterType.BEFORE_REDRAW, function () {
754
 
            _runBeforeRedrawQueue();
755
 
            return false;
756
 
        });
757
 
    }
758
 
}
759
 
 
760
 
/**
761
 
 * initializeDeferredWork:
762
 
 * @actor: A #ClutterActor
763
 
 * @callback: Function to invoke to perform work
764
 
 *
765
 
 * This function sets up a callback to be invoked when either the
766
 
 * given actor is mapped, or after some period of time when the machine
767
 
 * is idle.  This is useful if your actor isn't always visible on the
768
 
 * screen (for example, all actors in the overview), and you don't want
769
 
 * to consume resources updating if the actor isn't actually going to be
770
 
 * displaying to the user.
771
 
 *
772
 
 * Note that queueDeferredWork is called by default immediately on
773
 
 * initialization as well, under the assumption that new actors
774
 
 * will need it.
775
 
 *
776
 
 * Returns: A string work identifer
777
 
 */
778
 
function initializeDeferredWork(actor, callback, props) {
779
 
    // Turn into a string so we can use as an object property
780
 
    let workId = '' + (++_deferredWorkSequence);
781
 
    _deferredWorkData[workId] = { 'actor': actor,
782
 
                                  'callback': callback };
783
 
    actor.connect('notify::mapped', function () {
784
 
        if (!(actor.mapped && _deferredWorkQueue.indexOf(workId) >= 0))
785
 
            return;
786
 
        _queueBeforeRedraw(workId);
787
 
    });
788
 
    actor.connect('destroy', function() {
789
 
        let index = _deferredWorkQueue.indexOf(workId);
790
 
        if (index >= 0)
791
 
            _deferredWorkQueue.splice(index, 1);
792
 
        delete _deferredWorkData[workId];
793
 
    });
794
 
    queueDeferredWork(workId);
795
 
    return workId;
796
 
}
797
 
 
798
 
/**
799
 
 * queueDeferredWork:
800
 
 * @workId: work identifier
801
 
 *
802
 
 * Ensure that the work identified by @workId will be
803
 
 * run on map or timeout.  You should call this function
804
 
 * for example when data being displayed by the actor has
805
 
 * changed.
806
 
 */
807
 
function queueDeferredWork(workId) {
808
 
    let data = _deferredWorkData[workId];
809
 
    if (!data) {
810
 
        let message = 'Invalid work id %d'.format(workId);
811
 
        logError(new Error(message), message);
812
 
        return;
813
 
    }
814
 
    if (_deferredWorkQueue.indexOf(workId) < 0)
815
 
        _deferredWorkQueue.push(workId);
816
 
    if (data.actor.mapped) {
817
 
        _queueBeforeRedraw(workId);
818
 
        return;
819
 
    } else if (_deferredTimeoutId == 0) {
820
 
        _deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
821
 
            _runAllDeferredWork();
822
 
            _deferredTimeoutId = 0;
823
 
            return false;
824
 
        });
825
 
    }
826
 
}