[gnome-shell/wip/background-rework: 3/4] LayoutManager: implement new design for the startup animation



commit f2838770273e62b58fdc4e86da3888a2411295db
Author: Giovanni Campagna <gcampagna src gnome org>
Date:   Mon Dec 24 15:46:21 2012 +0100

    LayoutManager: implement new design for the startup animation
    
    The design calls for two different startup animations, in the GDM
    and in the normal session case.
    In both cases, we implement fading from the previous situation
    by acquiring the root background pixmap and turning it into a TFP
    texture, which is then animated and blended by Clutter as normal.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=682429

 js/ui/layout.js |  121 ++++++++++++++++++++++++++++++++++++++----------------
 js/ui/main.js   |    9 ++++
 2 files changed, 94 insertions(+), 36 deletions(-)
---
diff --git a/js/ui/layout.js b/js/ui/layout.js
index eb909af..73ec3d5 100644
--- a/js/ui/layout.js
+++ b/js/ui/layout.js
@@ -1,6 +1,7 @@
 // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 
 const Clutter = imports.gi.Clutter;
+const ClutterX11 = imports.gi.ClutterX11;
 const GObject = imports.gi.GObject;
 const Lang = imports.lang;
 const Mainloop = imports.mainloop;
@@ -15,9 +16,8 @@ const Params = imports.misc.params;
 const Tweener = imports.ui.tweener;
 
 const HOT_CORNER_ACTIVATION_TIMEOUT = 0.5;
-const STARTUP_ANIMATION_TIME = 0.2;
+const STARTUP_ANIMATION_TIME = 0.5;
 const KEYBOARD_ANIMATION_TIME = 0.15;
-const PLYMOUTH_TRANSITION_TIME = 1;
 const DEFAULT_BACKGROUND_COLOR = Clutter.Color.from_pixel(0x2e3436ff);
 
 const MonitorConstraint = new Lang.Class({
@@ -110,6 +110,7 @@ const LayoutManager = new Lang.Class({
         this._leftPanelBarrier = 0;
         this._rightPanelBarrier = 0;
         this._trayBarrier = 0;
+        this._startupPrepared = false;
 
         // Normally, the stage is always covered so Clutter doesn't need to clear
         // it; however it becomes visible during the startup animation
@@ -164,7 +165,7 @@ const LayoutManager = new Lang.Class({
                               Lang.bind(this, this._panelBoxChanged));
 
         this.trayBox = new St.Widget({ name: 'trayBox',
-                                       layout_manager: new Clutter.BinLayout() }); 
+                                       layout_manager: new Clutter.BinLayout() });
         this.addChrome(this.trayBox);
 
         this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox',
@@ -187,8 +188,6 @@ const LayoutManager = new Lang.Class({
     // yet when the LayoutManager was constructed.
     init: function() {
         this._chrome.init();
-
-        this._startupAnimation();
     },
 
     _updateMonitors: function() {
@@ -383,52 +382,102 @@ const LayoutManager = new Lang.Class({
         return this._keyboardIndex;
     },
 
-    _startupAnimation: function() {
-        this.panelBox.translation_y = -this.panelBox.height;
+    _acquireStillFrame: function() {
+        let frame = Shell.util_get_still_frame();
+        let texture = ClutterX11.TexturePixmap.new_with_pixmap(frame);
+        // The texture size might not match the screen size, for example
+        // if the session has a different XRandR configuration than the greeter
+        texture.x = 0;
+        texture.y = 0;
+        texture.width = global.screen_width;
+        texture.height = global.screen_height;
+        texture.set_automatic(true);
+
+        this._stillFrameTexture = texture;
+    },
+
+    // Startup Animations
+    //
+    // We have two different animations, depending on whether we're a greeter
+    // or a normal session.
+    //
+    // In the greeter, we want to animate the panel from the top, and smoothly
+    // fade the login dialog on top of whatever plymouth left on screen, which we
+    // grab as a X11 texture_from_pixmap.
+    // Here we just have the code to animate the panel, the login dialog animation
+    // is handled by modalDialog.js
+    //
+    // In a normal session, we want to take the root background, which now holds
+    // the final frame of the GDM greeter, and slide it from the bottom, while
+    // at the same time scaling the UI contents of the new shell on top of the
+    // stage background.
+    //
+    // Usually, we don't want to paint the stage background color because the
+    // MetaBackgroundActor inside global.window_group covers the entirety of the
+    // screen. So, we set no_clear_hint at the end of the animation.
+
+    prepareStartupAnimation: function() {
+        if (this._startupPrepared)
+            return;
+
+        // Set ourselves to FULLSCREEN input mode while the animation is running
+        // so events don't get delivered to X11 windows (which are distorted by the animation)
+        global.stage_input_mode = Shell.StageInputMode.FULLSCREEN;
 
-        let plymouthTransitionRunning = false;
+        this._acquireStillFrame();
 
-        // If we're the greeter, put up the xrootpmap actor
-        // and fade it out to have a nice transition from plymouth
-        // to the greeter. Otherwise, we'll just animate the panel,
-        // as usual.
         if (Main.sessionMode.isGreeter) {
-            this._background = Meta.BackgroundActor.new_for_screen(global.screen);
-            if (this._background != null) {
-                this._layoutManager.add_actor(this._background);
-                Tweener.addTween(this._background,
-                                 { opacity: 0,
-                                   time: PLYMOUTH_TRANSITION_TIME,
-                                   transition: 'linear',
-                                   onComplete: this._fadeBackgroundComplete,
-                                   onCompleteScope: this });
-                plymouthTransitionRunning = true;
-            }
+            global.stage.insert_child_below(this._stillFrameTexture, null);
+
+            this.panelBox.translation_y = -this.panelBox.height;
+        } else {
+            global.stage.insert_child_below(this._stillFrameTexture, null);
+
+            this.uiGroup.set_pivot_point(0.5, 0.5);
+            this.uiGroup.scale_x = this.uiGroup.scale_y = 0;
         }
 
-        if (!plymouthTransitionRunning)
-            this._fadeBackgroundComplete();
+        this._startupPrepared = true;
     },
 
-    _fadeBackgroundComplete: function() {
-        // Don't animate the strut
-        this._chrome.freezeUpdateRegions();
+    startupAnimation: function() {
+        if (Main.sessionMode.isGreeter)
+            this._startupAnimationGreeter();
+        else
+            this._startupAnimationSession();
+    },
 
-        if (this._background != null) {
-            this._background.destroy();
-            this._background = null;
-        }
+    _startupAnimationGreeter: function() {
+         this._chrome.freezeUpdateRegions();
+         Tweener.addTween(this.panelBox,
+                          { translation_y: 0,
+                            time: STARTUP_ANIMATION_TIME,
+                            transition: 'easeOutQuad',
+                            onComplete: this._startupAnimationComplete,
+                            onCompleteScope: this });
+    },
 
-        Tweener.addTween(this.panelBox,
-                         { translation_y: 0,
+    _startupAnimationSession: function() {
+        this._chrome.freezeUpdateRegions();
+        Tweener.addTween(this.uiGroup,
+                         { scale_x: 1,
+                           scale_y: 1,
                            time: STARTUP_ANIMATION_TIME,
                            transition: 'easeOutQuad',
                            onComplete: this._startupAnimationComplete,
-                           onCompleteScope: this
-                         });
+                           onCompleteScope: this });
     },
 
     _startupAnimationComplete: function() {
+        // At this point, the UI group is covering everything, so
+        // we no longer need to clear the stage
+        global.stage.no_clear_hint = true;
+
+        global.stage_input_mode = Shell.StageInputMode.NORMAL;
+
+        this._stillFrameTexture.destroy();
+        this._stillFrameTexture = null;
+
         this.emit('panel-box-changed');
         this._chrome.thawUpdateRegions();
     },
diff --git a/js/ui/main.js b/js/ui/main.js
index c15b6da..f7b7b65 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -81,6 +81,7 @@ let _cssStylesheet = null;
 let _overridesSettings = null;
 
 function _sessionUpdated() {
+    layoutManager.prepareStartupAnimation()
     wm.setCustomKeybindingHandler('panel-main-menu',
                                   KeybindingMode.NORMAL |
                                   KeybindingMode.OVERVIEW,
@@ -184,6 +185,14 @@ function start() {
 
     ExtensionDownloader.init();
     ExtensionSystem.init();
+
+    // Run the startup animation as soon as the mainloop is idle enough
+    // This is necessary to have it smooth and without interruptions from
+    // completed IO tasks
+    GLib.idle_add(GLib.PRIORITY_LOW, function() {
+        layoutManager.startupAnimation();
+        return false;
+    });
 }
 
 let _workspaces = [];



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]