[gnome-shell/wip/wobbly] wobbly



commit c0f8a3466cf2c6f694995393cef5bb76f3e21f50
Author: Jasper St. Pierre <jstpierre mecheye net>
Date:   Sun Aug 31 16:50:50 2014 -0700

    wobbly

 js/js-resources.gresource.xml |    1 +
 js/ui/windowManager.js        |    3 +
 js/ui/wobbly.js               |  130 ++++++++++++++++++++++++
 src/Makefile.am               |    2 +
 src/shell-gliding-effect.c    |  218 +++++++++++++++++++++++++++++++++++++++++
 src/shell-gliding-effect.h    |   50 ++++++++++
 6 files changed, 404 insertions(+), 0 deletions(-)
---
diff --git a/js/js-resources.gresource.xml b/js/js-resources.gresource.xml
index c99189c..4206f4c 100644
--- a/js/js-resources.gresource.xml
+++ b/js/js-resources.gresource.xml
@@ -90,6 +90,7 @@
     <file>ui/windowAttentionHandler.js</file>
     <file>ui/windowMenu.js</file>
     <file>ui/windowManager.js</file>
+    <file>ui/wobbly.js</file>
     <file>ui/workspace.js</file>
     <file>ui/workspaceSwitcherPopup.js</file>
     <file>ui/workspaceThumbnail.js</file>
diff --git a/js/ui/windowManager.js b/js/ui/windowManager.js
index 101f9a8..9873a1f 100644
--- a/js/ui/windowManager.js
+++ b/js/ui/windowManager.js
@@ -17,6 +17,7 @@ const Main = imports.ui.main;
 const ModalDialog = imports.ui.modalDialog;
 const Tweener = imports.ui.tweener;
 const WindowMenu = imports.ui.windowMenu;
+const Wobbly = imports.ui.wobbly;
 
 const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings';
 const MAXIMIZE_WINDOW_ANIMATION_TIME = 0.15;
@@ -811,6 +812,8 @@ const WindowManager = new Lang.Class({
         gesture = new AppSwitchAction();
         gesture.connect('activated', Lang.bind(this, this._switchApp));
         global.stage.add_action(gesture);
+
+        this._wobblerManager = new Wobbly.WindowWobblerManager();
     },
 
     _lookupIndex: function (windows, metaWindow) {
diff --git a/js/ui/wobbly.js b/js/ui/wobbly.js
new file mode 100644
index 0000000..bf3cbd5
--- /dev/null
+++ b/js/ui/wobbly.js
@@ -0,0 +1,130 @@
+// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
+
+const Clutter = imports.gi.Clutter;
+const Lang = imports.lang;
+const Meta = imports.gi.Meta;
+const Shell = imports.gi.Shell;
+
+function clampAbs(v, cap) {
+    if (v > cap)
+        v = cap;
+    if (v < -cap)
+        v = -cap;
+    return v;
+}
+
+const ActorWobbler = new Lang.Class({
+    Name: 'ActorWobbler',
+
+    _init: function(actor) {
+        this._actor = actor;
+        this._effect = new Shell.GlidingEffect();
+        this._actor.add_effect(this._effect);
+        this._actor._wobbler = this;
+
+        this._currentBend = 0;
+        this._currentHeightOffset = 0;
+        this._running = false;
+
+        this._allocationChangedId = this._actor.connect('allocation-changed', Lang.bind(this, 
this._allocationChanged));
+
+        this._timeline = new Clutter.Timeline({ duration: 100, repeat_count: -1 });
+        this._timeline.connect('new-frame', Lang.bind(this, this._newFrame));
+        this._timeline.start();
+    },
+
+    start: function() {
+        this._running = true;
+    },
+
+    stop: function() {
+        this._running = false;
+    },
+
+    _destroy: function() {
+        this._timeline.run_dispose();
+        this._timeline = null;
+
+        this._actor.disconnect(this._allocationChangedId);
+        this._actor.scale_y = 1.0;
+        this._actor.remove_effect(this._effect);
+        this._actor._wobbler = null;
+    },
+
+    _newFrame: function() {
+        this._step();
+    },
+
+    _step: function() {
+        const DAMPEN = 0.9;
+        this._currentBend *= DAMPEN;
+        this._currentHeightOffset *= DAMPEN;
+
+        // Cap the bend to a 100px shift.
+        const BEND_CAP = 100;
+        this._currentBend = clampAbs(this._currentBend, BEND_CAP);
+        if (Math.abs(this._currentBend) < 1)
+            this._currentBend = 0;
+        this._effect.set_bend_x(this._currentBend);
+
+        // Cap the height change to 25px in either direction.
+        const HEIGHT_OFFSET_CAP = 25;
+        this._currentHeightOffset = clampAbs(this._currentHeightOffset, HEIGHT_OFFSET_CAP);
+        if (Math.abs(this._currentHeightOffset) < 1)
+            this._currentHeightOffset = 0;
+        let [minHeight, natHeight] = this._actor.get_preferred_height(-1);
+        let scale = (natHeight + this._currentHeightOffset) / natHeight;
+        this._actor.scale_y = scale;
+
+        if (!this._running && this._currentBend == 0 && this._currentHeightOffset == 0)
+            this._destroy();
+    },
+
+    _allocationChanged: function(actor, box, flags) {
+        if (!this._running)
+            return;
+
+        if (this._oldX) {
+            let deltaX = box.x1 - this._oldX;
+            // Every 2px the user moves the window, we bend it by 1px.
+            this._currentBend -= deltaX / 2;
+        }
+
+        if (this._oldY) {
+            let deltaY = box.y1 - this._oldY;
+            // Every 2px the user moves the window, we scale it by 1px.
+            this._currentHeightOffset -= deltaY / 2;
+        }
+
+        this._oldX = box.x1;
+        this._oldY = box.y1;
+    },
+});
+
+const WindowWobblerManager = new Lang.Class({
+    Name: 'WindowWobblerManager',
+
+    _init: function() {
+        global.display.connect('grab-op-begin', Lang.bind(this, this._grabOpBegin));
+        global.display.connect('grab-op-end', Lang.bind(this, this._grabOpEnd));
+    },
+
+    _grabOpBegin: function(display, screen, window, op) {
+        if (op != Meta.GrabOp.MOVING)
+            return;
+
+        let actor = window.get_compositor_private();
+        if (!actor._wobbler)
+            new ActorWobbler(actor);
+        actor._wobbler.start();
+    },
+
+    _grabOpEnd: function(display, screen, window, op) {
+        if (!window)
+            return;
+
+        let actor = window.get_compositor_private();
+        if (actor._wobbler)
+            actor._wobbler.stop();
+    },
+});
diff --git a/src/Makefile.am b/src/Makefile.am
index abffd89..cce2ee0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -86,6 +86,7 @@ shell_public_headers_h =              \
        shell-app-usage.h               \
        shell-embedded-window.h         \
        shell-generic-container.h       \
+       shell-gliding-effect.h          \
        shell-glsl-quad.h               \
        shell-gtk-embed.h               \
        shell-global.h                  \
@@ -126,6 +127,7 @@ libgnome_shell_base_la_SOURCES =    \
        shell-embedded-window-private.h \
        shell-embedded-window.c         \
        shell-generic-container.c       \
+       shell-gliding-effect.c          \
        shell-glsl-quad.c               \
        shell-invert-lightness-effect.c \
        shell-keyring-prompt.h          \
diff --git a/src/shell-gliding-effect.c b/src/shell-gliding-effect.c
new file mode 100644
index 0000000..8a975a6
--- /dev/null
+++ b/src/shell-gliding-effect.c
@@ -0,0 +1,218 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2014 Endless Mobile
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ *     Jasper St. Pierre <jstpierre mecheye net>
+ */
+
+#include "config.h"
+
+#include "shell-gliding-effect.h"
+
+struct _ShellGlidingEffectPrivate
+{
+  int bend_x;
+
+  int tex_width, tex_height;
+  CoglPipeline *pipeline;
+
+  int tex_width_uniform;
+  int bend_x_uniform;
+};
+typedef struct _ShellGlidingEffectPrivate ShellGlidingEffectPrivate;
+
+G_DEFINE_TYPE_WITH_PRIVATE (ShellGlidingEffect, shell_gliding_effect, CLUTTER_TYPE_OFFSCREEN_EFFECT);
+
+static const gchar *gliding_decls =
+  "uniform int tex_width;\n"
+  "uniform int bend_x;\n";
+static const gchar *gliding_pre =
+  "float bend_x_coord = (float(bend_x) / tex_width);\n"
+  "float interp = (1 - cos(cogl_tex_coord.y * 3.1415926)) / 2;\n"
+  "cogl_tex_coord.x -= (interp * bend_x_coord);\n";
+
+/* XXX - clutter_effect_get_paint_volume is fucking terrible
+ * and I want to kill myself */
+static gboolean
+shell_gliding_effect_get_paint_volume (ClutterEffect      *effect,
+                                       ClutterPaintVolume *volume)
+{
+  ShellGlidingEffect *self = SHELL_GLIDING_EFFECT (effect);
+  ShellGlidingEffectPrivate *priv = shell_gliding_effect_get_instance_private (self);
+
+  float cur_width;
+  cur_width = clutter_paint_volume_get_width (volume);
+  cur_width += ABS (priv->bend_x);
+  clutter_paint_volume_set_width (volume, cur_width);
+
+  /* Also modify the origin if it bends to the left. */
+  if (priv->bend_x < 0)
+    {
+      ClutterVertex origin;
+      clutter_paint_volume_get_origin (volume, &origin);
+      origin.x += priv->bend_x;
+      clutter_paint_volume_set_origin (volume, &origin);
+    }
+
+  return TRUE;
+}
+
+static gboolean
+shell_gliding_effect_pre_paint (ClutterEffect *effect)
+{
+  ShellGlidingEffect *self = SHELL_GLIDING_EFFECT (effect);
+  ShellGlidingEffectPrivate *priv = shell_gliding_effect_get_instance_private (self);
+
+  if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)))
+    return FALSE;
+
+  /* If we're not doing any bending, we're not needed. */
+  if (priv->bend_x == 0)
+    return FALSE;
+
+  if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL))
+    {
+      /* if we don't have support for GLSL shaders then we
+       * forcibly disable the ActorMeta
+       */
+      g_warning ("Unable to use the ClutterBrightnessContrastEffect: the "
+                 "graphics hardware or the current GL driver does not "
+                 "implement support for the GLSL shading language. The "
+                 "effect will be disabled.");
+      clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), FALSE);
+      return FALSE;
+    }
+
+  if (!CLUTTER_EFFECT_CLASS (shell_gliding_effect_parent_class)->pre_paint (effect))
+    return FALSE;
+
+  ClutterOffscreenEffect *offscreen_effect = CLUTTER_OFFSCREEN_EFFECT (effect);
+  CoglObject *texture;
+
+  texture = clutter_offscreen_effect_get_texture (offscreen_effect);
+  cogl_pipeline_set_layer_texture (priv->pipeline, 0, texture);
+
+  priv->tex_width = cogl_texture_get_width (texture);
+  priv->tex_height = cogl_texture_get_height (texture);
+
+  /* priv->tex_width is the actor's paint volume, which includes our bent offset. Subtract that out. */
+  int original_width = priv->tex_width - priv->bend_x;
+  cogl_pipeline_set_uniform_1i (priv->pipeline, priv->tex_width_uniform, original_width);
+
+  return TRUE;
+}
+
+static void
+shell_gliding_effect_paint_target (ClutterOffscreenEffect *effect)
+{
+  ShellGlidingEffect *self = SHELL_GLIDING_EFFECT (effect);
+  ShellGlidingEffectPrivate *priv = shell_gliding_effect_get_instance_private (self);
+  CoglFramebuffer *fb = cogl_get_draw_framebuffer ();
+  ClutterActor *actor;
+  guint8 paint_opacity;
+
+  actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect));
+  paint_opacity = clutter_actor_get_paint_opacity (actor);
+
+  cogl_pipeline_set_color4ub (priv->pipeline,
+                              paint_opacity,
+                              paint_opacity,
+                              paint_opacity,
+                              paint_opacity);
+
+  cogl_framebuffer_draw_rectangle (fb, priv->pipeline,
+                                   0, 0, priv->tex_width, priv->tex_height);
+}
+
+static void
+shell_gliding_effect_dispose (GObject *object)
+{
+  ShellGlidingEffect *self = SHELL_GLIDING_EFFECT (object);
+  ShellGlidingEffectPrivate *priv = shell_gliding_effect_get_instance_private (self);
+
+  g_clear_pointer (&priv->pipeline, cogl_object_unref);
+
+  G_OBJECT_CLASS (shell_gliding_effect_parent_class)->dispose (object);
+}
+
+static void
+shell_gliding_effect_class_init (ShellGlidingEffectClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass);
+  ClutterOffscreenEffectClass *offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass);
+
+  offscreen_class->paint_target = shell_gliding_effect_paint_target;
+  effect_class->pre_paint = shell_gliding_effect_pre_paint;
+  effect_class->get_paint_volume = shell_gliding_effect_get_paint_volume;
+  object_class->dispose = shell_gliding_effect_dispose;
+}
+
+static void
+update_uniforms (ShellGlidingEffect *self)
+{
+  ShellGlidingEffectPrivate *priv = shell_gliding_effect_get_instance_private (self);
+  cogl_pipeline_set_uniform_1i (priv->pipeline, priv->bend_x_uniform, priv->bend_x);
+}
+
+static void
+shell_gliding_effect_init (ShellGlidingEffect *self)
+{
+  static CoglPipeline *pipeline_template;
+
+  ShellGlidingEffectPrivate *priv = shell_gliding_effect_get_instance_private (self);
+
+  if (G_UNLIKELY (pipeline_template == NULL))
+    {
+      CoglSnippet *snippet;
+      CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ());
+
+      pipeline_template = cogl_pipeline_new (ctx);
+
+      snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, gliding_decls, NULL);
+      cogl_snippet_set_pre (snippet, gliding_pre);
+      cogl_pipeline_add_layer_snippet (pipeline_template, 0, snippet);
+      cogl_object_unref (snippet);
+
+      cogl_pipeline_set_layer_null_texture (pipeline_template,
+                                            0, /* layer number */
+                                            COGL_TEXTURE_TYPE_2D);
+    }
+
+  priv->pipeline = cogl_pipeline_copy (pipeline_template);
+  priv->tex_width_uniform = cogl_pipeline_get_uniform_location (priv->pipeline, "tex_width");
+  priv->bend_x_uniform = cogl_pipeline_get_uniform_location (priv->pipeline, "bend_x");
+
+  update_uniforms (self);
+}
+
+void
+shell_gliding_effect_set_bend_x (ShellGlidingEffect *self,
+                                 int                 bend_x)
+{
+  ShellGlidingEffectPrivate *priv = shell_gliding_effect_get_instance_private (self);
+
+  if (priv->bend_x == bend_x)
+    return;
+
+  priv->bend_x = bend_x;
+  update_uniforms (self);
+  clutter_effect_queue_repaint (CLUTTER_EFFECT (self));
+}
diff --git a/src/shell-gliding-effect.h b/src/shell-gliding-effect.h
new file mode 100644
index 0000000..d165c0a
--- /dev/null
+++ b/src/shell-gliding-effect.h
@@ -0,0 +1,50 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+
+/*
+ * Copyright (C) 2014 Endless Mobile
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by:
+ *     Jasper St. Pierre <jstpierre mecheye net>
+ */
+
+#include <clutter/clutter.h>
+
+#define SHELL_TYPE_GLIDING_EFFECT             (shell_gliding_effect_get_type ())
+#define SHELL_GLIDING_EFFECT(obj)             (G_TYPE_CHECK_INSTANCE_CAST ((obj), SHELL_TYPE_GLIDING_EFFECT, 
ShellGlidingEffect))
+#define SHELL_GLIDING_EFFECT_CLASS(klass)     (G_TYPE_CHECK_CLASS_CAST ((klass),  SHELL_TYPE_GLIDING_EFFECT, 
ShellGlidingEffectClass))
+#define SHELL_IS_GLIDING_EFFECT(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SHELL_TYPE_GLIDING_EFFECT))
+#define SHELL_IS_GLIDING_EFFECT_CLASS(klass)  (G_TYPE_CHECK_CLASS_TYPE ((klass),  SHELL_TYPE_GLIDING_EFFECT))
+#define SHELL_GLIDING_EFFECT_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj),  SHELL_TYPE_GLIDING_EFFECT, 
ShellGlidingEffectClass))
+
+typedef struct _ShellGlidingEffect        ShellGlidingEffect;
+typedef struct _ShellGlidingEffectClass   ShellGlidingEffectClass;
+
+struct _ShellGlidingEffect
+{
+  ClutterOffscreenEffect parent;
+};
+
+struct _ShellGlidingEffectClass
+{
+  ClutterOffscreenEffectClass parent_class;
+};
+
+GType shell_gliding_effect_get_type (void) G_GNUC_CONST;
+
+void shell_gliding_effect_set_bend_x (ShellGlidingEffect *self,
+                                      int                 bend_x);


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