diff --git a/data/magCursors/default-cursor.xpm b/data/magCursors/default-cursor.xpm
new file mode 100644
index 0000000..045a719
--- /dev/null
+++ b/data/magCursors/default-cursor.xpm
@@ -0,0 +1,70 @@
+/* XPM */
+static char* Normal[] = {
+"64 64 3 1 16 6",
+"  c None",
+"! c #ffffff",
+"# c #000000",
+"                                                                ",
+"                                                                ",
+"                                                                ",
+"                                                                ",
+"                                                                ",
+"                                                                ",
+"                !!                                              ",
+"                !#!                                             ",
+"                !##!                                            ",
+"                !###!                                           ",
+"                !####!                                          ",
+"                !#####!                                         ",
+"                !######!                                        ",
+"                !#######!                                       ",
+"                !########!                                      ",
+"                !#########!                                     ",
+"                !##########!                                    ",
+"                !###########!                                   ",
+"                !############!                                  ",
+"                !#############!                                 ",
+"                !##############!                                ",
+"                !###############!                               ",
+"                !################!                              ",
+"                !#################!                             ",
+"                !##################!                            ",
+"                !###################!                           ",
+"                !####################!                          ",
+"                !#####################!                         ",
+"                !######################!                        ",
+"                !#######################!                       ",
+"                !########################!                      ",
+"                !#########################!                     ",
+"                !##########################!                    ",
+"                !###########################!                   ",
+"                !############################!                  ",
+"                !################!!!!!!!!!!!!!                  ",
+"                !################!                              ",
+"                !################!!                             ",
+"                !#################!                             ",
+"                !########!!#######!!                            ",
+"                !#######!  !#######!                            ",
+"                !######!   !#######!!                           ",
+"                !#####!     !#######!                           ",
+"                !####!      !#######!!                          ",
+"                !###!        !#######!                          ",
+"                !##!         !#######!!                         ",
+"                !#!           !#######!                         ",
+"                !!            !#######!!                        ",
+"                !              !#######!                        ",
+"                               !#######!!                       ",
+"                                !#######!                       ",
+"                                !#######!!                      ",
+"                                 !#######!                      ",
+"                                 !#######!                      ",
+"                                  !######!                      ",
+"                                   !!!!!!!                      ",
+"                                                                ",
+"                                                                ",
+"                                                                ",
+"                                                                ",
+"                                                                ",
+"                                                                ",
+"                                                                ",
+"                                                                "};
diff --git a/js/ui/altTab.js b/js/ui/altTab.js
index a1a5f16..f38efc7 100644
--- a/js/ui/altTab.js
+++ b/js/ui/altTab.js
@@ -60,7 +60,7 @@ AltTabPopup.prototype = {
         // the switcher appears underneath the current pointer location
         this._disableHover();
 
-        global.stage.add_actor(this.actor);
+        global.uiGroup.add_actor (this.actor);
     },
 
     show : function(backward) {
diff --git a/js/ui/magnifier.js b/js/ui/magnifier.js
new file mode 100755
index 0000000..66720e9
--- /dev/null
+++ b/js/ui/magnifier.js
@@ -0,0 +1,687 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const DBus = imports.dbus;
+const Gtk = imports.gi.Gtk;
+const Gdk = imports.gi.Gdk;
+const Clutter = imports.gi.Clutter;
+const Shell = imports.gi.Shell;
+const Big = imports.gi.Big;
+const Lang = imports.lang;
+const Mainloop = imports.mainloop;
+const MagnifierDBus = imports.ui.magnifierDBus;
+
+const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
+DEFAULT_BACKGROUND_COLOR.from_pixel (0x2266bbff);
+
+// Default settings
+const DEFAULT_X_MAGFACTOR = 2;          // Magnification factor:  2x.
+const DEFAULT_Y_MAGFACTOR = 2;
+const DEFAULT_MOUSE_POLL_TIME = 50;     // Frequency to poll current mouse location.
+const DEFAULT_OVERLAY_MOUSE = false;    // Place magnified view over current mouse.
+
+// Position, width and height of the magnified view.
+// Left edge, two-thirds down; half the width, and one-third the height.
+const FIXED_POS_SIZE = {
+    left: 0,
+    top: Shell.Global.get().screen_height/3 * 2,
+    width: Shell.Global.get().screen_width/2,
+    height: Shell.Global.get().screen_height/3
+};
+
+// Border size for magnified view
+const BORDER_WIDTH = 8;
+
+// Screen Positions
+const FULL              = "Full Screen";
+const TOP               = "Top Half";
+const BOTTOM            = "Bottom Half";
+const LEFT              = "Left Half";
+const RIGHT             = "Right Half";
+const FIXED             = "Fixed";
+const SCREEN_POSITIONS  = [ FULL, TOP, BOTTOM, LEFT, RIGHT, FIXED ];
+
+const global = Shell.Global.get();
+let magDBusService = null;
+
+function Magnifier (options) {
+    this._init (options);
+}
+
+Magnifier.prototype = {
+    _init: function (options) {
+
+        // Initialized with a preferences UI?
+        this.prefsUI = ((options && options.prefsUI) ? options.prefsUI : null);
+
+        // The root actor for the magnifier is a Big.Box with a frame around it.
+        const BORDER_COLOR = new Clutter.Color();
+        BORDER_COLOR.from_pixel(0x800000ff);
+        this._magView = new Big.Box ({
+            border: BORDER_WIDTH,
+            border_color: BORDER_COLOR,
+            name: "MagnifierRoot"
+        });
+
+        // Append a Clutter.Group to clip the contents of the magnified vew.
+        // Note:  by default, the Clutter.Group constructor sets the default
+        // size to (0,0); but that causes the height to not clip properly when
+        // added to _magView.  Setting temporarily to (1,1) fixes this.
+        let aGroup = new Clutter.Group({
+            clip_to_allocation: true,
+            width: 1,
+            height: 1,
+            name: "MagGroup"
+        });
+        this._magView.append (aGroup, Big.BoxPackFlags.EXPAND);
+
+        // Add a background for when the magnified uiGroup is scrolled
+        // out of view (don't want to see desktop showing through).
+        let background = new Clutter.Rectangle ({ color: DEFAULT_BACKGROUND_COLOR, name: "MagBkgrnd" });
+        aGroup.add_actor (background);
+
+        // Clone the group that contains all of UI on the screen.  This is the
+        // chrome, the windows, etc.
+        this._uiGroupClone = new Clutter.Clone ({ source: global.uiGroup, name: "uiGroupClone" });
+        aGroup.add_actor (this._uiGroupClone);
+
+        // store <aGroup> as a local property of _magView for easy access.
+        this._magView.mainGroup = aGroup;
+
+        // The contents of the clone are scaled from their original size
+        // to the allocated size of the clone, this is not useful to us
+        // since we want to scale by a fixed factor; the size of the window
+        // group is unpredictable because windows can go outside the
+        // screen, but we can set it to something fixed, since the size
+        // of a clutter actor doesn't affect clipping or display. Then we
+        // set the size of the clone to the same size. Actual size picked
+        // doesn't matter - use the size of the screen.
+        global.uiGroup.set_size (global.screen_width, global.screen_height);
+        background.set_size (global.screen_width, global.screen_height);
+        this._uiGroupClone.set_size (global.screen_width, global.screen_height);
+
+        // Add the magnified mouse cursor.
+        this.setMouseCursor ("default-cursor.xpm");
+
+        // Initial magnification factor, and add the magnified view to the
+        // stage.
+        this._uiGroupClone.set_scale (DEFAULT_X_MAGFACTOR, DEFAULT_Y_MAGFACTOR);
+        this._magView.hide();
+        global.stage.add_actor (this._magView);
+
+        // Default position and size of magnified view -- bottom half of screen.
+        // Note:  should get this from preferences.
+        this.setBottomHalf();
+
+        // Add the preferences window, if none assigned yet.
+        if (!this.prefsUI) {
+            this.prefsUI = new MagPrefsUI ({magnifier: this});
+        }
+
+        // Track the mouse (default).
+        this.startTrackingMouse();
+
+        // Export to dbus.
+        global.grab_dbus_service();
+        magDBusService = new MagnifierDBus.ShellMagnifier();
+        DBus.session.flush();
+
+    },  // end _init()
+
+    /**
+     * setActive:
+     * @activate  Boolean to activate of de-activate the Magnifier.
+     *            The magnified view is shown or hidden depending
+     *            on the sense of the argument, but remains an
+     *            actor on the stage.
+     */
+    setActive: function (activate) {
+        if (activate) {
+          this._magView.show();
+        }
+        else {
+          this._magView.hide();
+        }
+    },   // end setActive()
+
+    /**
+     * isActive:
+     * @return  Whether the magnifier is active (boolean).
+     */
+    isActive: function() {
+        let visible = this._magView.get_flags() & Clutter.ActorFlags.VISIBLE;
+        return (visible == Clutter.ActorFlags.VISIBLE);
+
+    },   // end isActive().
+
+    /**
+     * setMagFactor:
+     * @inXmagFactor    the power to set the horizontal magnification factor to
+     *                  of the magnified view, e.g. 2x, 10x, etc.  If less than
+     *                  or equal to zero, it is treated as 1x.
+     * @inYmagFactor    the power to set the vertical magnification factor to
+     *                  of the magnified view.  If less thanor equal to zero,
+     *                  it is treated as 1x.
+     */
+    setMagFactor: function (inXMagFactor, inYMagFactor) {
+        let x = ( inXMagFactor <= 0 ? 1 : inXMagFactor );
+        let y = ( inYMagFactor <= 0 ? 1 : inYMagFactor );
+        this._uiGroupClone.set_scale (x, y);
+
+    },   // end setMagFactor().
+
+    /**
+     * getMagFactor:
+     * @return  an array, {x, y}, that contains the horizontal and vertical
+     *          magnficaction factors (2x, 10X, etc.).
+     */
+    getMagFactor: function() {
+        return this._uiGroupClone.get_scale();
+
+    },   // end getMagFactor().
+
+    /**
+     * setSize:
+     * @inWidth     The width to set the magnified view to.
+     * @inHeight    The height to set the magnified view to.
+     */
+    setSize: function (inWidth, inHeight) {
+        this._magView.set_size (inWidth, inHeight);
+
+        // Reposition the mouse to the centre.
+        let [cx, cy] = this.getCenter();
+        this._cursorActor.set_position (cx, cy);
+
+    },   // end setSize().
+
+    /**
+     * getSize:
+     * @return  an array, [width, height], that specifies the size of the
+     *          magnified view.
+     */
+    getSize: function() {
+        return this._magView.get_size();
+
+    },   // end getSize().
+
+    /**
+     * setPosition:
+     * @inX     The x-coord of the new position.
+     * @inY     The y-coord of the new position.
+     * @return  The old position [x, y].
+     */
+    setPosition: function (inX, inY) {
+        let [x, y] = this._magView.get_position();
+        this._magView.set_position (inX, inY);
+        return [x, y];
+
+    },   // end setPosition().
+
+    /**
+     * getPosition:
+     * @return  an array, [x, y], that gives the position of the
+     *          magnified view.
+     */
+    getPosition: function() {
+        return this._magView.get_position();
+
+    },   // end getPosition().
+
+    /**
+     * getCenter:
+     * @return  an array, [x, y], that gives the centre of the
+     *          magnified view.
+     */
+    getCenter: function() {
+        let [x, y] = this._magView.get_size();
+        return [x/2, y/2];
+
+    },   // end getPosition().
+
+    /**
+     * isFullScreenMode:
+     * Does the magnified view occupy the whole screen?
+     */
+    isFullScreenMode: function() {
+        let retVal = false;
+        let [x, y] = this._magView.get_position();
+        if (x == 0 && y == 0) {
+            [x, y] = this._magView.get_size();
+            if (x == global.screen_width && y == global.screen_height) {
+                retVal = true;
+            }
+        }
+        return retVal;
+
+    },   // end isFullScreenMode().
+
+    /**
+     * scrollToPosition:
+     * Centre the contents of the magnified view to the given (non-magnifed)
+     * coordinates.
+     * @inX:        The x-coord of the position to scroll to.
+     * @inY:        The y-coord of the position to scroll to.
+     * @hideMouse:  Hide the magnified mouse cursor after the move.  The
+     *              default is *not* hide.  Optional.
+     * @return  an array, [x, y], of the new position of the clone of
+     * the UI group
+     */
+    scrollToPosition: function (inX, inY, hideMouse) {
+        // coords in _uiGroupClone are scaled by the magnification factor.
+        let [X, Y] = this.getMagFactor();
+        let MX = inX * X;
+        let MY = inY * Y;
+
+        // get the centre of the magnified view, move UI clone to centre
+        // based on given location, and return new position.
+        let [cx, cy] = this.getCenter();
+        this._uiGroupClone.set_position (cx - MX, cy - MY);
+
+        // If the magnified view is to overlay the current mouse location,
+        // then move it such that it is centred over the mouse. Don't do
+        // this in full screen mode, however.
+        if (this._overlayMouse && !this.isFullScreenMode()) {
+            this.setPosition (inX - cx, inY - cy);
+        }
+
+        // Show/hide mouse as requested.
+        ( hideMouse ? this._cursorActor.hide() : this._cursorActor.show() );
+
+        return this._uiGroupClone.get_position();
+
+    },   // end scrollToPosition().
+
+    /**
+     * getROI:
+     * Retrieves the "region of interest" -- the rectangular bounds of that part
+     * of the desktop that the magnified view is showing
+     * (left, top, width, height).  The bounds are given in non-magnified
+     * coordinates.
+     * @return  an array, [left, top, width, height], representing the bounding
+     *          rectangle of what is shown in the magnified view.
+     */
+    getROI: function() {
+        // Get the coordinates of the centre of the magnified view and convert
+        // to unmagnified.
+        let [MX, MY] = this._uiGroupClone.get_position();
+        let [cx, cy] = this.getCenter();
+        let [xFactor, yFactor] = this.getMagFactor();
+        let xCentre = (cx - MX) / xFactor;
+        let yCentre = (cy - MY) / yFactor;
+
+        // Get the (width, height) of the magnified display, and scale that by
+        // the magnification factor.
+        let [width, height] = this.getSize();
+        width /= xFactor;
+        height /= yFactor;
+        let left = xCentre - width/2;
+        let top = yCentre - height/2;
+
+        return [left, top, width, height];
+
+    },   // end getROI().
+
+    /**
+     * setMouseCursor:
+     * Set the magnified mouse cursor.
+     * @filename    The name of the file containing the cursor image.  It is
+     *              assumed to reside in
+     *              <code>global.imagedir/magCursors</code>.
+     */
+    setMouseCursor: function (filename) {
+        let uri = "file://" + global.imagedir + "magCursors/" + filename;
+        let cursorImg = Shell.TextureCache.get_default().load_uri_sync (
+            Shell.TextureCachePolicy.FOREVER, uri, 64, 64
+        );
+        let cursorActor = new Big.Box();
+        cursorActor.append (cursorImg, Big.BoxPackFlags.EXPAND);
+        this._cursorActor = cursorActor;
+        this._magView.add_actor (this._cursorActor);
+        let [cx, cy] = this.getCenter();
+        this._cursorActor.set_position (cx, cy);
+
+    },   // setMouseCursor().
+
+    /**
+     * setOverlayMouse:
+     * Set the sense of the overlay such that either the magnified view is
+     * centred over the mouse, or it is in a fixed position.  If the
+     * magnifier is in full screen mode, then a "true" argument is ignored.
+     * @inFlag  A boolean to set the overlay mouse mode with.
+     */
+    setOverlayMouse: function (inFlag) {
+        let fullScreen = this.isFullScreenMode();
+        this._overlayMouse = (inFlag && !fullScreen);
+
+        // Reset to magnified view to current position if not overlaying
+        // mouse, and not full screen.
+        if (!this._overlayMouse && !fullScreen) {
+            this.setScreenPosition (this._screenPosition);
+        }
+    },   // end setOverlayMouse().
+
+    /**
+     * isOverlayMouse:
+     * Get the sense of the overlay such that either the magnified view is
+     * centred over the mouse, or it is in a fixed position.  If the 
+     * magnifier is in full screen mode, then this is always "false".
+     * @return  The overlay mode as a boolean.
+     */
+    isOverlayMouse: function() {
+        return this._overlayMouse;
+
+    },   // end isOverlayMouse().
+
+    /**
+     * setTopHalf:
+     * Magnifier view occupies the top half of the screen.
+     */
+    setTopHalf: function() {
+        this.setPosition (0, 0);
+        this.setSize (global.screen_width, global.screen_height/2);
+        this._screenPosition = TOP;
+
+    },  // end setTopHalf().
+
+    /**
+     * setBottomHalf:
+     * Magnifier view occupies the bottom half of the screen.
+     */
+    setBottomHalf: function() {
+        this.setPosition (0, global.screen_height/2);
+        this.setSize (global.screen_width, global.screen_height/2);
+        this._screenPosition =  BOTTOM;
+
+    },  // end setBottomHalf().
+
+    /**
+     * setLeftHalf:
+     * Magnifier view occupies the left half of the screen.
+     */
+    setLeftHalf: function() {
+        this.setPosition (0, 0);
+        this.setSize (global.screen_width/2, global.screen_height);
+        this._screenPosition = LEFT;
+
+    },  // end setLeftHalf().
+
+    /**
+     * setRightHalf:
+     * Magnifier view occupies the right half of the screen.
+     */
+    setRightHalf: function() {
+        this.setPosition (global.screen_width/2, 0);
+        this.setSize (global.screen_width/2, global.screen_height);
+        this._screenPosition = RIGHT;
+
+    },  // end setRightHalf().
+
+    /**
+     * setFixed:
+     * Magnifier view occupies a certain percentage of the screen.
+     */
+    setFixed: function() {
+        this.setPosition (FIXED_POS_SIZE.left, FIXED_POS_SIZE.top);
+        this.setSize (FIXED_POS_SIZE.width, FIXED_POS_SIZE.height);
+        this._screenPosition = FIXED;
+
+    },  // setFixed().
+
+    /**
+     * getScreenPosition:
+     * Tell the outside world what the current mode is -- magnifiying the
+     * top half, bottom half, etc.
+     * @return:  the current mode.
+     */
+     getScreenPosition: function() {
+        return this._screenPosition;
+     },
+
+    /**
+     * startTrackingMouse:
+     * Turn on mouse tracking, if not already doing so.
+     */
+    startTrackingMouse: function() {
+        // initialize previous mouse coord to undefined.
+        var prevCoord = { x: NaN, y: NaN };
+        var id = this.mouseTrackingId;
+        if (!id || id < 0) {
+            this._mouseTrackingId = Mainloop.timeout_add (
+                DEFAULT_MOUSE_POLL_TIME,
+                Lang.bind (this, this.scrollToMousePos, prevCoord)
+            );
+        }
+    },  // end this.startTrackingMouse().
+
+    /**
+     * stopTrackingMouse:
+     * Turn on mouse tracking, if not already doing so.
+     */
+    stopTrackingMouse: function() {
+        if (this._mouseTrackingId) {
+            Mainloop.source_remove (this._mouseTrackingId);
+        }
+        this._mouseTrackingId = -1;
+
+    },   // end this.stopTrackingMouse().
+
+    /**
+     * isTrackingMouse:
+     * Is the magnifier tracking the mouse currently?
+     */
+    isTrackingMouse: function() {
+        return this._mouseTrackingId != -1;
+
+    },   // end this.startTrackingMouse().
+
+    /**
+     * scrollToMousePos:
+     * Centre the magnified view to the position of the mouse.
+     * @inPrevCoord:    The previous mouse coordinates.  Used for stop
+     *                  scrolling if the new position is the same as the last
+     *                  one.  Note: optional.
+     * @return  an array, [x, y], of the new position of the clone of
+     * the UI group
+     */
+    scrollToMousePos: function (inPrevCoord) {
+        let [objUnder, mx, my, mask] =
+            Gdk.Screen.get_default().get_root_window().get_pointer();
+        if (!inPrevCoord) {
+            return this.scrollToPosition (mx, my);
+        }
+        else if (inPrevCoord.x != mx || inPrevCoord.y != my) {
+            inPrevCoord.x = mx;
+            inPrevCoord.y = my;
+            return this.scrollToPosition (mx, my);
+        }
+        else {
+             return [mx, my];
+        }
+    },  // end scrollToMousePos().
+
+    /**
+     * setFullScreenMode:
+     * Set the Magnifier to full-screen mode.
+     * Note:  disallows magnified view to overlay the mouse.
+     */
+    setFullScreenMode: function() {
+        if (!this.isFullScreenMode()) {
+            this.setPosition (0, 0);
+            this.setSize (global.screen_width, global.screen_height);
+            this.setOverlayMouse (false);
+        }
+    },   // end setFullScreenMode().
+
+    /**
+     * resetFullScreenMode:
+     * Reset the size of the magnifier view back to the size before
+     * it was set to full screen mode.
+     */
+    resetFullScreenMode: function() {
+        this.setScreenPosition (this.getScreenPosition);
+    },
+
+    /**
+     * fullScreenCapable:
+     * Can the magnified view occupy the full screen?
+     * @return  true if it can or false otherwise.
+     */
+    fullScreenCapable: function() {
+        return true;
+    },
+
+    /**
+     * setScreenPosition:
+     * General way to set full-screen, top-half, bottom-half, left-half,
+     * right-half, or fixed mode.
+     */
+    setScreenPosition: function (inPosition) {
+        if (inPosition == FULL) {
+            this.setFullScreenMode();
+        }
+        else if (inPosition == TOP) {
+            this.setTopHalf();
+        }
+        else if (inPosition == BOTTOM) {
+            this.setBottomHalf();
+        }
+        else if (inPosition == LEFT) {
+            this.setLeftHalf();
+        }
+        else if (inPosition == RIGHT) {
+            this.setRightHalf();
+        }
+        else if (inPosition == FIXED) {
+            this.setFixed();
+        }
+    },   // end setScreenPosition().
+
+    /**
+     * moveCenterTo:
+     * Move the magnified view such it is centred on the given point.
+     * @inX     The x-coord of the point to centre on.
+     * @inY     The y-coord of the point to centre on.
+     * @return  the old position [x, y].
+     */
+    moveCenterTo: function (inX, inY) {
+        let [cx, cy] = this.getCenter();
+        return this.setPosition (inX - cx, inY - cy);
+
+    },  // end moveCenterTo().
+
+    /**
+     * scrollContentsTo:
+     * Shift the contents of the magnified view such it is centred on the given
+     * coordinate.  Also, hide the magnified mouse image in the process.
+     * @inX     The x-coord of the point to centre on.
+     * @inY     The y-coord of the point to centre on.
+     * @return  The new position of the UI group clone.
+     */
+    scrollContentsTo: function (inX, inY) {
+        return this.scrollToPosition (inX, inY, true);
+
+    }   // scrollContentsTo().
+
+}   // end Magnifier.prototype = ...
+
+/**
+ * UI for magnifier preferences.
+ */
+
+function MagPrefsUI (options) {
+    this._init (options);
+}
+
+MagPrefsUI.prototype = {
+    _init: function (options) {
+        var _magnifier;
+        if (options && options.magnifier) {
+            _magnifier = options.magnifier;
+        }
+        else {
+            _magnifier = new Magnifier ({ prefsUI: this });
+        }
+        this.win = new Gtk.Window ({ type: Gtk.WindowType.TOPLEVEL });
+        this.win.set_border_width (10);
+        this.win.set_title ("Magnifier Preferences");
+        this.win.set_deletable (false);
+
+        // A vertical box to contain the controls
+        let vBox = new Gtk.VButtonBox();
+
+        // Creates a toggle button to flip whether magnifier is active or not.
+        let togButton = new Gtk.ToggleButton({ label: "Show _Magnifier" });
+        togButton.set_use_underline (true);
+        togButton.set_active (_magnifier.isActive());
+        togButton.connect("clicked", function() {
+            _magnifier.setActive (!(_magnifier.isActive()));
+        });
+        vBox.add (togButton);
+
+        // A checkbox for overlay mode (magnified view overlays current mouse
+        // position.
+        let overlayCheckbox = new Gtk.CheckButton ({ label: "Movable lens" });
+        overlayCheckbox.set_use_underline (true);
+        overlayCheckbox.set_sensitive (! _magnifier.isFullScreenMode());
+        overlayCheckbox.set_active (_magnifier.isOverlayMouse());
+        overlayCheckbox.connect ("clicked", function() {
+            _magnifier.setOverlayMouse (! _magnifier.isOverlayMouse());
+        });
+
+        // Creates a drop down menu to choose among: full screen mode,
+        // bottom half, top half, left half, or right half of screen.
+        let screenPosition =  Gtk.ComboBox.new_text();
+        screenPosition.set_title ("Position:");
+        let currentPosition = _magnifier.getScreenPosition();
+        let currentPositionIdx = -1;
+        for (let i = 0; i < SCREEN_POSITIONS.length; i++) {
+            screenPosition.append_text (SCREEN_POSITIONS[i]);
+            if (currentPosition == SCREEN_POSITIONS[i]) {
+                currentPositionIdx = i;
+            }
+        }
+        screenPosition.set_active (currentPositionIdx);
+        screenPosition.connect ("changed", function() {
+            let mode = screenPosition.get_active_text();
+            if (mode == FULL) {
+                overlayCheckbox.set_active (false);
+                overlayCheckbox.set_sensitive (false);
+            }
+            else {
+                overlayCheckbox.set_sensitive (true);
+            }
+            _magnifier.setScreenPosition (mode);
+        });
+        let aLabel = new Gtk.Label ({ label: screenPosition.get_title() });
+        aLabel.set_alignment (0, 0);
+        let aBox = new Gtk.HBox();
+        aBox.add (aLabel);
+        aBox.add (screenPosition);
+        vBox.add (aBox);
+
+        // A slider for adjusting the mag factor.
+        let slider = new Gtk.HScale();
+        slider.set_range (1, 10);
+        let [magX, magY] = _magnifier.getMagFactor();
+        slider.set_value (magX);
+        slider.connect ("value-changed", function() {
+            let val = slider.get_value();
+            _magnifier.setMagFactor (val, val);
+        });
+        vBox.add (slider);
+        vBox.add (overlayCheckbox);
+
+        // Put the controls in the window, and show it.
+        this.win.add (vBox);
+        this.win.show_all();
+        let [cx, cy] = _magnifier.getSize();
+        let [mx, my] = _magnifier.getMagFactor();
+        this.win.move ((cx/mx)/2, (cy/my)/2);
+
+    }   // end _init().
+
+}   // end MagPrefsUI.prototype = ...
+
+
+
+
+
diff --git a/js/ui/magnifierDBus.js b/js/ui/magnifierDBus.js
new file mode 100755
index 0000000..48e0459
--- /dev/null
+++ b/js/ui/magnifierDBus.js
@@ -0,0 +1,152 @@
+/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
+
+const DBus = imports.dbus;
+const Shell = imports.gi.Shell;
+
+const MagnifierIface = {
+    name: "org.gnome.Shell.Magnifier",
+    methods: [{ name: "setActive",
+                inSignature: "b",
+                outSignature: ""
+              },
+              { name: "isActive",
+                inSignature: "",
+                outSignature: "b"
+              },
+              { name: "getScreenSize",
+                inSignature: "",
+                outSignature: "(ii)"
+              },
+              { name: "setScreenPosition",
+                inSignature: "s",
+                outSignature: ""
+              },
+              { name: "getMagFactor",
+                inSignature: "",
+                outSignature: "dd"
+              },
+              { name: "setMagFactor",
+                inSignature: "dd",
+                outSignature: ""
+              },
+              { name: "shiftContentsTo",
+                inSignature: "ii",
+                outSignature: "ai"
+              },
+              { name: "getROI",
+                inSignature: "",
+                outSignature: "(iiii)"
+              }
+             ],
+    signals: [],
+    properties: []
+};
+
+let global = Shell.Global.get();
+
+function ShellMagnifier() {
+    this._init();
+}
+
+ShellMagnifier.prototype = {
+    _init: function() {
+        DBus.session.exportObject('/org/gnome/Shell/Magnifier', this);
+    },
+
+    /**
+     * setActive:
+     * @activate  Boolean to activate of de-activate the Magnifier.
+     *            The magnified view is shown or hidden depending
+     *            on the sense of the argument, but remains an
+     *            actor on the stage.
+     */
+    setActive: function (activate) {
+        global.magnifier.setActive (activate);
+
+    },   // end setActive()
+
+    /**
+     * isActive:
+     * @return  Whether the magnifier is active (boolean).
+     */
+    isActive: function() {
+        return global.magnifier.isActive ();
+
+    },   // end isActive()
+
+    /**
+     * getScreenSize:
+     * Retrieves the size of the screen
+     * @return  an array, [width, height], representing the size of the screen
+     */
+    getScreenSize: function() {
+        return [global.screen_width, global.screen_height];
+
+    },   // end getScreenSize()
+
+    /**
+     * setScreenPosition:
+     * Positions the magnifier to one of the following positions on the
+     * screen: FULL, TOP, BOTTOM, LEFT, RIGHT, FIXED
+     * @position:  one of "Full Screen", "Top Half", "Bottom Half", 
+     *             "Left Half", "Right Half", or "Fixed"
+     */
+    setScreenPosition: function (position) {
+        global.magnifier.setScreenPosition(position)
+
+    },   // end setScreenPosition()
+
+    /**
+     * getMagFactor:
+     * @return  an array, {x, y}, that contains the horizontal and vertical
+     *          magnficaction factors (2x, 10X, etc.).
+     */
+    getMagFactor: function() {
+        return global.magnifier.getMagFactor();
+
+    },   // end getMagFactor().
+
+    /**
+     * setMagFactor:
+     * @inXmagFactor    the power to set the horizontal magnification factor to
+     *                  of the magnified view, e.g. 2x, 10x, etc.  If less than
+     *                  or equal to zero, it is treated as 1x.
+     * @inYmagFactor    the power to set the vertical magnification factor to
+     *                  of the magnified view.  If less thanor equal to zero,
+     *                  it is treated as 1x.
+     */
+    setMagFactor: function (inXMagFactor, inYMagFactor) {
+        global.magnifier.setMagFactor(inYMagFactor, inYMagFactor)
+
+    },   // end setMagFactor().
+
+    /**
+     * getROI:
+     * Retrieves the "region of interest" -- the rectangular bounds of that part
+     * of the desktop that the magnified view is showing
+     * (left, top, width, height).  The bounds are given in non-magnified
+     * coordinates.
+     * @return  an array, [left, top, width, height], representing the bounding
+     *          rectangle of what is shown in the magnified view.
+     *
+     */
+    getROI: function() {
+        return global.magnifier.getROI();
+
+    },
+
+    /**
+     * shiftContentsTo:
+     * Shift the contents of the magnified view such it is centred on the given
+     * coordinate.
+     * @x:      x-coordinate to scroll to.
+     * @y:      y-coordinate to scroll to.
+     * @return  The new position (top left corner) of the magnified view.
+     */
+    shiftContentsTo: function (x, y) {
+        return global.magnifier.scrollContentsTo (x,y);
+
+    }
+};
+
+DBus.conformExport (ShellMagnifier.prototype, MagnifierIface);
diff --git a/js/ui/main.js b/js/ui/main.js
index aac2672..64e510e 100644
--- a/js/ui/main.js
+++ b/js/ui/main.js
@@ -21,6 +21,7 @@ const LookingGlass = imports.ui.lookingGlass;
 const ShellDBus = imports.ui.shellDBus;
 const Sidebar = imports.ui.sidebar;
 const WindowManager = imports.ui.windowManager;
+const Magnifier = imports.ui.magnifier;
 
 const DEFAULT_BACKGROUND_COLOR = new Clutter.Color();
 DEFAULT_BACKGROUND_COLOR.from_pixel(0x2266bbff);
@@ -36,6 +37,8 @@ let recorder = null;
 let shellDBusService = null;
 let modalCount = 0;
 let modalActorFocusStack = [];
+let uiGroup = null;
+let magnifier = null;
 
 function start() {
     // Add a binding for "global" in the global JS namespace; (gjs
@@ -102,6 +105,16 @@ function start() {
     sidebar = new Sidebar.Sidebar();
     wm = new WindowManager.WindowManager();
 
+    // Set up stage hierarchy for the Magnifier -- it
+    // clones everything but the Magnifier actor itself.
+    //
+    uiGroup = new Clutter.Group ({ name: "uiGroup"});
+    global.window_group.reparent (uiGroup);
+    global.overlay_group.reparent (uiGroup);
+    chrome.actor.reparent (uiGroup);
+    global.stage.add_actor (uiGroup);
+    global.uiGroup = uiGroup;
+
     global.screen.connect('toggle-recording', function() {
         if (recorder == null) {
             recorder = new Shell.Recorder({ stage: global.stage });
@@ -124,6 +137,13 @@ function start() {
 
     global.stage.connect('captured-event', _globalKeyPressHandler);
 
+    // Install magnifier.
+    // Note:  to make active, call magnifier.setActive (true);
+    //
+    magnifier = new Magnifier.Magnifier();
+    global.magnifier = magnifier;
+
+
     Mainloop.idle_add(_removeUnusedWorkspaces);
 }
 
diff --git a/src/shell-global.c b/src/shell-global.c
old mode 100644
new mode 100755
index b07d090..54eca78
--- a/src/shell-global.c
+++ b/src/shell-global.c
@@ -21,6 +21,7 @@
 #include <gjs/gjs.h>
 
 #define SHELL_DBUS_SERVICE "org.gnome.Shell"
+#define MAGNIFIER_DBUS_SERVICE "org.gnome.Shell.Magnifier"
 
 static void grab_notify (GtkWidget *widget, gboolean is_grab, gpointer user_data);
 
@@ -694,6 +695,20 @@ shell_global_grab_dbus_service (ShellGlobal *global)
           exit (1);
         }
     }
+
+  /* ...and the org.gnome.Shell.Magnifier service.
+   */
+  if (!dbus_g_proxy_call (bus, "RequestName", &error,
+                          G_TYPE_STRING, MAGNIFIER_DBUS_SERVICE,
+                          G_TYPE_UINT, 0,
+                          G_TYPE_INVALID,
+                          G_TYPE_UINT, &request_name_result,
+                          G_TYPE_INVALID))
+    {
+      g_print ("failed to acquire %s: %s\n", MAGNIFIER_DBUS_SERVICE, error->message);
+      /* Failing to acquire the magnifer service is not fatal.  Log the error,
+       * but keep going. */
+    }
   g_object_unref (bus);
 }
 
