Translate

Archives

More GNOME Shell Customization

If you plan to customize the GNOME Shell in any meaningful way, you need to understand the technologies underlying the GNOME Shell and understand how to write a GNOME Shell extension to provide the customization that you require. In this post I delve deeper into the technologies behind the new GNOME Shell and provide sample code for a number of simple extensions which demonstrate how to customize and extend various components of the GNOME Shell user interface.

Essentially, the GNOME Shell is an integrated window manager, compositor, and application launcher. It acts as a composting window manager for the desktop displaying both application windows and other objects in a Clutter scene graph. Most of the UI code is written in JavaScript which accesses Clutter and other underlying libraries via GObject Introspection.

Here is a block diagram of the underlying technologies that support the GNOME Shell as of v3.0:
GNOME3 Shell screenshot
This is a modified version of a diagram that exists on the GNOME website.

The GNOME Shell uses OpenGL to render graphics. OpenGL uses a hardware accelerated pixel format by default but can support software rendering. However, hardware acceleration is required to run the GNOME Shell as it uses a number of 3D capabilities to accelerate the transforms. Most graphics cards less than 3 years old should support hardware acceleration. If hardware acceleration is unavailable, the GNOME Shell defaults back to a modified version of the GNOME 2 Panel. See Vincent Untz’s post for further information on this fallback mode. In addition, you can force the GNOME Shell to use the fallback mode via a switch in the Settings, System Info panel. Whether a graphics card supports hardware acceleration or not is determined by a helper application /usr/libexec/gnome-session-is-accelerated which returns either 0 or 1. Another limitation in the 3.0 version of the GNOME shell is that the total horizontal or vertical pixel count of all your monitors must be less than 2048.

int
main (int argc, char **argv)
{
        Display *display = NULL;
        int      ret = 1;

       display = XOpenDisplay (NULL);
        if (!display) {
                _print_error ("No X display.");
                goto out;
        }

        if (_has_composite (display) != 0) {
                _print_error ("No composite extension.");
                goto out;
        }

        if (_has_hardware_gl (display) != 0) {
                _print_error ("No hardware 3D support.");
                goto out;
        }

        if (_has_texture_from_pixmap (display) != 0) {
                _print_error ("No GLX_EXT_texture_from_pixmap support.");
                goto out;
        }

        if (_is_max_texture_size_big_enough (display) != 0) {
                _print_error ("GL_MAX_TEXTURE_SIZE is too small.");
                goto out;
        }

        ret = 0;

out:
        if (display)
                XCloseDisplay (display);

        return ret;
}


As you can see, the use of gotos is alive and well! If you are certain that your graphics card supports hardware acceleration but for some reason GNOME Shell only works in fallback mode, try replacing this application with a script that simply returns true. There is also a black list of graphic cards. According to Emmanuele Bassi:

the hard limit is on Intel 915/945 integrated graghics (which are sadly what netbooks generally use). The limit also used to exist on 965 but it was a soft limit that was lifted by fixing the driver in Mesa

You can also force the fallback mode using a gsettings key:

$ gsettings set org.gnome.desktop.session session-name gnome-fallback


Access to OpenGL is via Cogl which is a graphics API that exposes the features of 3D graphics hardware using a more object oriented design than OpenGL. The Clutter graphics library handles scene graphing. In Clutter, widgets are called actors, and windows are called stages. A Clutter application contains at least one stage containing actors such as rectangles, images, or text. A useful online resource for Clutter programming is Programming in Clutter by Murray Cumming. By the way, the Clutter library is also used in Moblin which, along with Maemo, is now part of Meego. Meego uses MX widgets on top of Clutter (a useful tutorial can be found here) whereas the GNOME Shell uses a Shell Toolkit (St) which implements many custom actors, such as containers, bins, boxes, buttons and scrollbars that are useful in implementing GNOME Shell UI features. The Shell Toolkit was derived from the Moblin UI Toolkit. See ../src/st in the gnome-shell GIT source code repository. The Shell Toolkit also implements CSS support (see ../src/st/st-theme*) which makes the GNOME Shell themeable. Generally if you see any object whole name starts with St. you can assume you are dealing with the Shell Toolkit. Accessibility is handled by Cally (Clutter Accessibility Implementation Library).

Window management is handled by a modified version of Metacity called Mutter. Before the introduction of Metacity in GNOME 2.2, GNOME used Enlightenment and then Sawfish as its window manager. Metacity uses the GTK+ graphical widget toolkit to create its user interface components, enabling it to be themeable and blend in with other GTK+ applications. Mutter is a newer compositing window manager based on Metacity and Clutter. Note that the GNOME Shell fallback mode still uses Metacity and Gtk+, whereas the normal hardware accelerated mode uses Mutter. By the way, the GNOME Shell is actually implemented as a Mutter plugin. You can obtain copious amounts of debug information from Mutter using imports.gi.Meta.set_verbose(true) or you can control the volume of debug information using the add_verbose_topic method, e.g. to view only window state use Meta = imports.gi.Meta; Meta.add_verbose_topic(Meta.DebugTopic.WINDOW_STATE).

The GObject Introspection layer sits on top of Mutter and the Shell toolkit. One way to look at this layer is to consider it a glue layer between the Mutter and Shell Tookit libraries and JavaScript. GObject Introspection is used to automatically generate the GObject bindings for JavaScript (gjs) which is the language used to implement the GNOME Shell UI. The actual version of the JavaScript language available using gjs is 1.8.5 as this JavaScript engine is based on Mozilla’s Spidermonkey JavaScript engine.

The goal of GObject Introspection is to describe the set of APIs in a uniform, machine readable XML format called GIR. A typelib is a compiled version of a GIR which is designed to be fast, memory efficient and complete enough so that language bindings can be written on top of it without other sources of information. You can examine the contents of a specific typelib file using g-ir-generate. For example, here is what is stored in the Shell Toolkit typelib file for st_texture_cache_load_uri_sync which I use in Example 7 below.

#  g-ir-generate /usr/lib64/gnome-shell/St-1.0.typelib 
....
      <method name="load_uri_sync" c:identifier="st_texture_cache_load_uri_sync" throws="1">
        <return-value transfer-ownership="none">
          <type name="Clutter.Actor"/>
        </return-value>
        <parameters>
          <parameter name="policy" transfer-ownership="none">
            <type name="TextureCachePolicy"/>
          </parameter>
          <parameter name="uri" transfer-ownership="none">
            <type name="utf8"/>
          </parameter>
          <parameter name="available_width" transfer-ownership="none">
            <type name="gint32"/>
          </parameter>
          <parameter name="available_height" transfer-ownership="none">
            <type name="gint32"/>
          </parameter>
        </parameters>
      </method>
....


You also need to understand the various components that make up the GNOME Shell UI if you are going to be successful in customizing the GNOME Shell. Here are the various components of the screen displayed just after you successfully log in.

GNOME3 Shell screenshot

Here is a description of the various components:

  • Top Bar (Also called Panel) – Horizontal bar at the top of the scrren. This is main access point to the shell. (../js/ui/panel.js)
  • Activities button (Also called Hotspot) – Button and Hot Corner that brings up the Overview (see below). (../js/ui/panel.js)
  • Application menu – Shows the name of the currently-focused application. You can click on it to get a menu of relevant actions. (../js/ui/panel.js)
  • Clock – Also contains the Calendar (../js/ui/dateMenu.js)
  • System Status Icons – Icons for accessibility options, Bluetooth, keyboard layout, etc. (../js/ui/status/*)
  • Status Menu – Also contains the user menu. Lets you change your IM status, personal preferences, and exit the session. (../js/ui/statusMenu.js)
  • Notifications Area – The part of the ephemeral horizontal bar at the bottom of screen where notifications are displayed. (../js/ui/notificationDaemon.js)
  • Message tray – The part of the ephemeral horizontal bar at the bottom of screen where pending notifications and other messages display. (../js/ui/messageTray.js)

All the JavaScript code referenced above and in the next section is under /usr/share/gnome-shell.

The Overview screen is what you see when you click on the Activities button. It is mainly implemented in ../js/ui/overview.js. It has the following UI components:

GNOME3 Shell screenshot

Here is a description of the various components in this particular screen:

  • Dash – Vertical bar on the left, that shows your favourite applications. (../js/ui/dash.js)
  • View Selector – Lets you pick between Windows and Applications. (../js/ui/viewSelector.js, ../js/ui/overview.js)
  • Search Entry – When you enter a string, various things (application names, documents, etc.) get searched. (../js/ui/viewSelector.js for the entry, and ../js/ui/searchDisplay.js for the display of search results)
  • Workspace List – Vertical bar on the right, with thumbnails for the active workspaces. (../js/ui/workspaceSwitcherPopup.js)

In the following examples, I demonstrate how to customize various components of the GNOME Shell UI using extensions or by directly modifying the source code as in Example 7 if an extension is not possible. I assume that you know JavaScript and the components that form a GNOME Shell extension.

Example 1:

The GNOME Shell developers are pushing hard to eliminate the traditional notification area on the top bar of GNOME desktops. However for the moment, tray icons are still displayed on the Panel to the left of the System Status area.

For example gnote normally displays in the GNOME Shell message area as shown below:
GNOME3 Shell screenshot

With this simple extension:

const Panel = imports.ui.panel;
const StatusIconDispatcher = imports.ui.statusIconDispatcher;

function main() {

    // add the notification(s) you want display on the top bar
    // - one per line. Use the english text string displayed when 
    // hovering your mouse over the bottom right notification area

    StatusIconDispatcher.STANDARD_TRAY_ICON_IMPLEMENTATIONS['gnote'] = 'gnote';

}


you can get gnote to display on the Panel.

GNOME3 Shell screenshot

Note how you import a module using the imports keyword. If you want to import a specific API version of a module, you can do so by specifying the required version number, e.g.

 
const Gtk = imports.gi.versions.Gtk = '3.0';

Example 2:

There is an existing extension in the gnome-shell-extensions source code repository called alternative status menu which replaces the GNOME Shell status menu in its entirety just so that the Power Off, Suspend and Hibernate menu options can be displayed instead of just the Suspend menu option.

GNOME3 Shell screenshot

Instead of the brute force approach used by this extension, the code shown below simply locates the suspend menu option and replaces it with the three required menu options and the auxiliary support functions.

const St = imports.gi.St;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const Shell = imports.gi.Shell;
const Lang = imports.lang;
const Gettext = imports.gettext.domain('gnome-shell');
const _ = Gettext.gettext;


function updateSuspend(object, pspec, item) {
    item.actor.visible = object.get_can_suspend();
}

function updateHibernate(object, pspec, item) {
    item.actor.visible = object.get_can_hibernate();
}

function onSuspendActivate(item) {
    Main.overview.hide();

    this._screenSaverProxy.LockRemote(Lang.bind(this, function() {
        this._upClient.suspend_sync(null);
    }));
}

function onHibernateActivate(item) {
    Main.overview.hide();

    this._screenSaverProxy.LockRemote(Lang.bind(this, function() {
        this._upClient.hibernate_sync(null);
    }));
}

function changeUserMenu()
{
    let children = this.menu._getMenuItems();
    for (let i = 0; i < children.length; i++) {
         let item = children[i];
         if (item.label) {
              let _label = item.label.get_text();
              // global.log("menu label: " + _label);
              if (_label == _("Suspend"))
                   item.destroy();
         }
    }

    let item = new PopupMenu.PopupMenuItem(_("Suspend"));
    item.connect('activate', Lang.bind(this, onSuspendActivate));
    this._upClient.connect('notify::can-suspend', Lang.bind(this, updateSuspend, item));
    updateSuspend(this._upClient, null, item);
    this.menu.addMenuItem(item);

    item = new PopupMenu.PopupMenuItem(_("Hibernate"));
    item.connect('activate', Lang.bind(this, onHibernateActivate));
    this._upClient.connect('notify::can-hibernate', Lang.bind(this, updateHibernate, item));
    updateHibernate(this._upClient, null, item);
    this.menu.addMenuItem(item);

    item = new PopupMenu.PopupMenuItem(_("Power Off..."));
    item.connect('activate', Lang.bind(this, function() {
        this._session.ShutdownRemote();
    }));
    this.menu.addMenuItem(item);

}

function main(metadata) {

    // Post 3.0  let statusMenu = Main.panel._userMenu;
    let statusMenu = Main.panel._statusmenu;
    changeUserMenu.call(statusMenu);

}


There are different types of power hibernation but the above example just uses the default method. Some people might find it useful to have a sleep menu option also.

Note that I have commented out a line of code in the main function. The commented out line is what you should use in post 3.0 versions of the GNOME Shell. How you access Panel objects is changing. See GNOME Bugzilla 646915 for full details. Essentially, a number of Panel objects have been renamed and a _statusArea object now points to status area PanelButton objects. The idea is that you will be able to address each Panel object consistently as follows:

    Main.panel._activities
    Main.panel._appMenu
    Main.panel._dateMenu
    Main.panel._statusArea.a11y
    Main.panel._statusArea.volume
    Main.panel._userMenu

Example 3:

In this example. I show you how to add a menu to the middle of the Panel as shown below:

GNOME3 Shell screenshot

Here is the source code for the extension used to create that menu:

const St = imports.gi.St;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const Gettext = imports.gettext;
const _ = Gettext.gettext;


function PlacesButton() {
    this._init();
}

PlacesButton.prototype = {
    __proto__: PanelMenu.Button.prototype,

    _init: function() {
        PanelMenu.Button.prototype._init.call(this, 0.0);

        this._label = new St.Label({ text: _("MyPlaces") });
        this.actor.set_child(this._label);
        Main.panel._centerBox.add(this.actor, { y_fill: true });

        let placeid;
        this.placeItems = [];

        this.defaultPlaces = Main.placesManager.getDefaultPlaces();
        this.bookmarks     = Main.placesManager.getBookmarks();
        this.mounts        = Main.placesManager.getMounts();

        // Display default places
        for ( placeid = 0; placeid < this.defaultPlaces.length; placeid++) {
            this.placeItems[placeid] = new PopupMenu.PopupMenuItem(_(this.defaultPlaces[placeid].name));
            this.placeItems[placeid].place = this.defaultPlaces[placeid];
            this.menu.addMenuItem(this.placeItems[placeid]);
            this.placeItems[placeid].connect('activate', function(actor,event) {
                actor.place.launch();
            });

        }

        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
        // Display default bookmarks 
        for ( let bookmarkid = 0; bookmarkid < this.bookmarks.length; bookmarkid++, placeid++) {
            this.placeItems[placeid] = new PopupMenu.PopupMenuItem(_(this.bookmarks[bookmarkid].name));
            this.placeItems[placeid].place = this.bookmarks[bookmarkid];
            this.menu.addMenuItem(this.placeItems[placeid]);
            this.placeItems[placeid].connect('activate', function(actor,event) {
                actor.place.launch();
            });
        }

        if (this.mounts.length > 0) {
            this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
        }

        // Display default mounts 
        for ( let mountid = 0; mountid < this.mounts.length; placeid++, mountid++ ) {
            this.placeItems[placeid] = new PopupMenu.PopupMenuItem(_(this.mounts[mountid].name));
            this.placeItems[placeid].place = this.mounts[mountid];
            this.menu.addMenuItem(this.placeItems[placeid]);
            this.placeItems[placeid].connect('activate', function(actor,event) {
                actor.place.launch();
            });
        }

        Main.panel._centerBox.add(this.actor, { y_fill: true });
        Main.panel._menus.addMenu(this.menu);

    }

};

function main(extensionMeta) {

    let userExtensionLocalePath = extensionMeta.path + '/locale';
    Gettext.bindtextdomain("places_button", userExtensionLocalePath);
    Gettext.textdomain("places_button");

    new PlacesButton();
}


Notice how you can retrieve details of all places, bookmarks and mounts from Main.placesManager:

places = Main.placesManager.getDefaultPlaces();
bookmarks  = Main.placesManager.getBookmarks();
mounts  = Main.placesManager.getMounts();

Example 4:

In this example, I show you how to extend the previous example to display icons on each menu option as shown below:

GNOME3 Shell screenshot

Here is the modified source code:

const St = imports.gi.St;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const Gettext = imports.gettext;
const _ = Gettext.gettext;

const MYPLACES_ICON_SIZE = 22;


function PlacesButton() {
    this._init();
}

PlacesButton.prototype = {
    __proto__: PanelMenu.Button.prototype,

    _init: function() {
        PanelMenu.Button.prototype._init.call(this, 0.0);

        this._label = new St.Label({ text: _("MyPlaces") });
        this.actor.set_child(this._label);
        Main.panel._centerBox.add(this.actor, { y_fill: true });

        let placeid;
        this.placeItems = [];

        this.defaultPlaces = Main.placesManager.getDefaultPlaces();
        this.bookmarks     = Main.placesManager.getBookmarks();
        this.mounts        = Main.placesManager.getMounts();

        // Display default places
        for ( placeid = 0; placeid < this.defaultPlaces.length; placeid++) {
            this.placeItems[placeid] = new PopupMenu.PopupMenuItem(_(this.defaultPlaces[placeid].name));
            let icon = this.defaultPlaces[placeid].iconFactory(MYPLACES_ICON_SIZE);
            this.placeItems[placeid].addActor(icon, { align: St.Align.END});
            this.menu.addMenuItem(this.placeItems[placeid]);
            this.placeItems[placeid].connect('activate', function(actor,event) {
                actor.place.launch();
            });

        }

        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());

        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());

        // Display default bookmarks 
        for ( let bookmarkid = 0; bookmarkid < this.bookmarks.length; bookmarkid++, placeid++) {
            this.placeItems[placeid] = new PopupMenu.PopupMenuItem(_(this.bookmarks[bookmarkid].name));
            let icon = this.bookmarks[bookmarkid].iconFactory(MYPLACES_ICON_SIZE);
            this.placeItems[placeid].addActor(icon, { align: St.Align.END});
            this.menu.addMenuItem(this.placeItems[placeid]);
            this.placeItems[placeid].connect('activate', function(actor,event) {
                actor.place.launch();
            });
        }

       if (this.mounts.length > 0) {
           this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
       }

        // Display default mounts 
        for ( let mountid = 0; mountid < this.mounts.length; placeid++, mountid++ ) {
            this.placeItems[placeid] = new PopupMenu.PopupMenuItem(_(this.mounts[mountid].name));
            let icon = this.mounts[mountid].iconFactory(MYPLACES_ICON_SIZE);
            this.placeItems[placeid].addActor(icon, { align: St.Align.END});
            this.placeItems[placeid].place = this.mounts[mountid];
            this.menu.addMenuItem(this.placeItems[placeid]);
            this.placeItems[placeid].connect('activate', function(actor,event) {
                actor.place.launch();
            });
        }

        Main.panel._centerBox.add(this.actor, { y_fill: true });
        Main.panel._menus.addMenu(this.menu);

    }

};


function main(extensionMeta) {

    let userExtensionLocalePath = extensionMeta.path + '/locale';
    Gettext.bindtextdomain("places_button", userExtensionLocalePath);
    Gettext.textdomain("places_button");

    new PlacesButton();
}

The heavy lifting in creating icons is done by iconFactory which is a JavaScript callback that creates an icon texture given a size parameter. It is implemented in ../js/ui/placeDisplay.js

iconFactory: function(size) {
        let icon = this._mount.get_icon();
        return St.TextureCache.get_default().load_gicon(null, icon, size);
},

Example 5:

In this example, I show you how to modify the previous example to display icons followed by labels on each menu option as shown below:

GNOME3 Shell screenshot

Here is the relevant source code:

const St = imports.gi.St;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;

const Gettext = imports.gettext;
const _ = Gettext.gettext;

const MYPLACES_ICON_SIZE = 22;


function MyPopupMenuItem() {
   this._init.apply(this, arguments);
}

MyPopupMenuItem.prototype = {
    __proto__: PopupMenu.PopupBaseMenuItem.prototype,

    _init: function(icon, text, params) {
          PopupMenu.PopupBaseMenuItem.prototype._init.call(this, params);

          this.addActor(icon);
          this.label = new St.Label({ text: text });
          this.addActor(this.label);
    }
};


function PlacesButton() {
    this._init();
}

function PlacesButton() {
    this._init();
}

PlacesButton.prototype = {
    __proto__: PanelMenu.Button.prototype,

    _init: function() {
        PanelMenu.Button.prototype._init.call(this, 0.0);

        this._icon = new St.Icon({ icon_name: 'start-here',
                                              icon_type: St.IconType.SYMBOLIC,
                                              style_class: 'system-status-icon' });
        this.actor.set_child(this._icon);
        Main.panel._centerBox.add(this.actor, { y_fill: true });

        let placeid;
        this.placeItems = [];

        this.defaultPlaces = Main.placesManager.getDefaultPlaces();
        this.bookmarks     = Main.placesManager.getBookmarks();
        this.mounts        = Main.placesManager.getMounts();

        // Display default places
        for ( placeid = 0; placeid < this.defaultPlaces.length; placeid++) {
            let icon = this.defaultPlaces[placeid].iconFactory(MYPLACES_ICON_SIZE);
            this.placeItems[placeid] = new MyPopupMenuItem(icon, _(this.defaultPlaces[placeid].name));

            this.menu.addMenuItem(this.placeItems[placeid]);
            this.placeItems[placeid].connect('activate', function(actor,event) {
                actor.place.launch();
            });

        }
        this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());

        // Display default bookmarks 
        for ( let bookmarkid = 0; bookmarkid < this.bookmarks.length; bookmarkid++, placeid++) {
            let icon = this.bookmarks[bookmarkid].iconFactory(MYPLACES_ICON_SIZE);
            this.placeItems[placeid] = new MyPopupMenuItem(icon, _(this.bookmarks[bookmarkid].name));

            this.menu.addMenuItem(this.placeItems[placeid]);
            this.placeItems[placeid].connect('activate', function(actor,event) {
                actor.place.launch();
            });
        }

       if (this.mounts.length > 0) {
           this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
       }

        // Display default mounts 
        for ( let mountid = 0; mountid < this.mounts.length; placeid++, mountid++ ) {
            let icon = this.mounts[mountid].iconFactory(MYPLACES_ICON_SIZE);
            this.placeItems[placeid] = new MyPopupMenuItem(icon, _(this.mounts[mountid].name) );
            this.placeItems[placeid].place = this.mounts[mountid];

            this.menu.addMenuItem(this.placeItems[placeid]);
            this.placeItems[placeid].connect('activate', function(actor,event) {
                actor.place.launch();
            });
        }

        Main.panel._centerBox.add(this.actor, { y_fill: true });
        Main.panel._menus.addMenu(this.menu);

    }

};

function main(extensionMeta) {

    let userExtensionLocalePath = extensionMeta.path + '/locale';
    Gettext.bindtextdomain("places_button", userExtensionLocalePath);
    Gettext.textdomain("places_button");

    new PlacesButton();
}


I had to implement my own version of PopupMenuItem called MyPopupMenuItem in order to display an icon in front on the menuitem label. This is basically just a wrapper around PopupBaseMenuItem.

Example 6:

In this example, I show you how to add an Applications menu next to the Activities button.

GNOME3 Shell screenshot

Here is the source code for extension.js:

const St = imports.gi.St;
const Main = imports.ui.main;
const PopupMenu = imports.ui.popupMenu;
const PanelMenu = imports.ui.panelMenu;
const Shell = imports.gi.Shell;
const Lang = imports.lang;

const Gettext = imports.gettext;
const _ = Gettext.gettext;

const APPMENU_ICON_SIZE = 22;


function MyPopupMenuItem() {
   this._init.apply(this, arguments);
}

MyPopupMenuItem.prototype = {
    __proto__: PopupMenu.PopupBaseMenuItem.prototype,

    _init: function(icon, text, menu_icon_first, params) {
        PopupMenu.PopupBaseMenuItem.prototype._init.call(this, params);

        this.label = new St.Label({ text: text });

        if (menu_icon_first) {
            this.box = new St.BoxLayout({ style_class: 'applications-menu-box'});
            this.box.add(icon);
            this.box.add(this.label);
            this.addActor(this.box);
        } else {
            this.addActor(this.label);
            this.addActor(icon);
        }
    }

};


function ApplicationsButton() {
   this._init.apply(this, arguments);
}

ApplicationsButton.prototype = {
    __proto__: PanelMenu.Button.prototype,

    _init: function(mode) {
        PanelMenu.Button.prototype._init.call(this, 0.0);

        this._icon = new St.Icon({ icon_name: 'fedora-logo-icon',
                                   icon_type: St.IconType.FULLCOLOR,
                                   icon_size: Main.panel.button.height });
        this.actor.set_child(this._icon);

        this._appSys = Shell.AppSystem.get_default();
        this._categories = this._appSys.get_sections();
        this._menuIconFirst = mode;

        this._display();

        this._appSys.connect('installed-changed', Lang.bind(this, function() {
            Main.queueDeferredWork(this._reDisplay);
        }));

        // add immediately after hotspot
        Main.panel._leftBox.insert_actor(this.actor, 1);
        Main.panel._menus.addMenu(this.menu);
    },

   _display: function() {
        this.appItems = [];

        for (let id = 0; id < this._categories.length; id++) {
            this.appItems[this._categories[id]] = new PopupMenu.PopupSubMenuMenuItem(this._categories[id]);
            this.menu.addMenuItem(this.appItems[this._categories[id]]);
        }

        let appInfos = this._appSys.get_flattened_apps().filter(function(app) {
            return !app.get_is_nodisplay();
        });


        for (let appid = appInfos.length-1; appid >= 0; appid--) {
            let appInfo = appInfos[appid];
            let sec = appInfo.get_section();
            if (sec != null) {
                let icon = appInfo.create_icon_texture(APPMENU_ICON_SIZE);
                let appName = new MyPopupMenuItem(icon, appInfo.get_name(), this._menuIconFirst);
                // let appName = new PopupMenu.PopupMenuItem(appInfo.get_name());
                appName._appInfo = appInfo;

                this.appItems[appInfo.get_section()].menu.addMenuItem(appName);
                appName.connect('activate', function(actor,event) {
                    let id = actor._appInfo.get_id();
                    Shell.AppSystem.get_default().get_app(id).activate(-1);
                });
           }
       }

    },

    _redisplay: function() {
        for (let id = 0; id < this._categories.length; id++) {
            this.appItems[this._categories[id]].menu.destroy();
        }
        this._display();
    }
};


function main(extensionMeta) {

    let userExtensionLocalePath = extensionMeta.path + '/locale';
    Gettext.bindtextdomain("applications_button", userExtensionLocalePath);
    Gettext.textdomain("applications_button");

    new ApplicationsButton(false);
}

Again I implemented my own version of PopupMenuItem called MyPopupMenuItem in order to handle displaying an icon in front on the menuitem label or visa versa as shown below. If false is passed to ApplicationsButton when creating the new object, menu options are displayed label followed by icon, otherwise they are displayed icon followed by label.

GNOME3 Shell screenshot

Example 7:

Not all components of the GNOME Shell can be easily modified or customized. For example, suppose I would like to display a search provider’s logo as an icon on their search provider button. The icon is already available as a base64 string in a search provider’s OpenSearch xml file but is not currently used by the GNOME Shell.

Here is how the search provider buttons look like at present:

GNOME3 Shell screenshot

Looking at the current source code for the GNOME Shell, we can see that in search.js the icon string is read by parse_search_provider and stored in the _providers array.

   _addProvider: function(fileName) {
        let path = global.datadir + '/search_providers/' + fileName;
        let source = Shell.get_file_contents_utf8_sync(path);
        let [success, name, url, langs, icon_uri] = global.parse_search_provider(source);

        let provider ={ name: name,
                        url: url,
                        id: this._providers.length,
                        icon_uri: icon_uri,
                        langs: langs };
        if (this._checkSupportedProviderLanguage(provider)) {
            this._providers.push(provider);
            this.emit('changed');
        }
    },


However it is not passed to where the search provider button is created in searchDisplay.js due to a limitation in the following piece of code in search.js:

    getProviders: function() {
        let res = [];
        for (let i = 0; i < this._providers.length; i++)
            res.push({ id: i, name: this._providers[i].name });
        return res;
    },


This needs to be modified to include the icon_uri base64 string, i.e.

    getProviders: function() {
        let res = [];
        for (let i = 0; i < this._providers.length; i++)
           res.push({ id: i, name: this._providers[i].name, icon_uri: this._providers[i].icon_uri }); 
        return res;
    },      


The next problem to overcome is how to decode the icon_uri base64 string and convert it into an actor.to displaying a search provider’s icon on their button. To do this you need to modify _createOpenSearchProviderButton in searchDisplay.js to display the icon as well as the name of the search provider.

Here is the original code

    _createOpenSearchProviderButton: function(provider) {
        let button = new St.Button({ style_class: 'dash-search-button',
                                     reactive: true,
                                     x_fill: true,
                                     y_align: St.Align.MIDDLE });
        let bin = new St.Bin({ x_fill: false,
                               x_align:St.Align.MIDDLE });
        button.connect('clicked', Lang.bind(this, function() {
            this._openSearchSystem.activateResult(provider.id);
        }));
        let title = new St.Label({ text: provider.name,
                                   style_class: 'dash-search-button-label' });

        bin.set_child(title);
        button.set_child(bin);
        provider.actor = button;

        this._searchProvidersBox.add(button);
    },


and here is the modified code which displays the search providers icon as well as their name.

    _createOpenSearchProviderButton: function(provider) {
        let button = new St.Button({ style_class: 'dash-search-button',
                                     reactive: true,
                                     x_fill: true,
                                     y_align: St.Align.MIDDLE });
        button.connect('clicked', Lang.bind(this, function() {
            this._openSearchSystem.activateResult(provider.id);
        }));
        let title = new St.Label({ text: provider.name,
                                   style_class: 'dash-search-button-label' });

        let textureCache = St.TextureCache.get_default();
        let searchIcon = textureCache.load_uri_sync(ST_TEXTURE_CACHE_POLICY_FOREVER,
                                                    provider.icon_uri, -1, -1);

        let iconBin = new St.Bin({ style_class: 'dash-search-button-icon',
                                   child: searchIcon });

        let box = new St.BoxLayout();
        box.add(iconBin, {expand: true, x_fill: false, x_align: St.Align.END });
        box.add(title, {expand: true, x_fill: false, x_align: St.Align.START });

        button.set_child(box);
        provider.actor = button;

        this._searchProvidersBox.add(button);
    },


With this modified code, here is how the search provider buttons now look like:

GNOME3 Shell screenshot

A GNOME Shell extension could probably be written to monkey patch the modified versions of the two functions into the GNOME Shell. It is something I will try to do when I get some free time.

Well, this post is getting too big and so it is time to conclude it. But before I do, I want to mention something about the GNOME Shell that has been on my mind recently as I experiment with its internals. For some reason the GNOME developers seem to think that the GNOME Shell should be an integral part of the OS and that is obvious in some of the design decisions. Recently Colin Walters stated that

Where we want to get to is that there are really just three things:

* Apps
* Extensions
* The OS

This is just plain wrong as far as I am concerned. A user should always have a choice of desktop managers and shells. Sounds like the vision of the GNOME developers, at least as articulated by Colin Walters in his post, is that the OS and the GNOME Shell should be one and the same as in Microsoft Windows. If this is the goal, then I fear that many existing users will abandon the GNOME desktop.

Sometimes I get the impression that the GNOME Shell was designed and put together by a group of arrogant young software engineers who were more interested in adhering to the tenants of so-called usability and design theory studies than in what their end users really needed in a modern graphical shell. Frankly, I fully agree with what Bruce Byfield recently wrote in Datamation about usability studies hurting the free desktop.

Fortunately, the decision to use JavaScript for the GNOME Shell UI may prove to be the salvation of the GNOME Shell as it allows ordinary users to quickly experiment with modifying parts of the UI. From what I see on the Internet, there are quite a few people experimenting with customizing the GNOME Shell either by directly modifying the source code or by developing extensions. While GNOME developers such as Lennart Poettering are advocating stricter vertical integration of the platform, I think that the decision to use JavaScript will be key to enabling users of GNOME Shell to easily bypass the diktats of the anointed few.

Don’t get me wrong – I like and use the GNOME Shell and really do not want to go back to GNOME 2 or another desktop manager, but I am often frustrated by it’s design constraints which get in the way of me doing what I want to do quickly and efficiently. Worse, when you look under the hood of the GNOME Shell, you quickly become aware of serious shortcomings in much of the underlying codebase. For example, why is the GNOME shell dependant on two distinct JavaScript engines, i.e. gjs and seed (webkit)? Pick one of these two JavaScript engines and remove the dependencies on the other. And why am I forced to use Evolution for calendaring? What happened to choice?

Adieu, and keep experimenting!

P.S. All of the extensions in this post will be downloadable from here.

88 comments to More GNOME Shell Customization

  • awesome post(s), thanks!

    i was hacking on the dock extension, trying to get it to the top panel. i succeeded but still have couple todos. today i was happy to see about

    Main.panel._leftBox.insert_actor(this.actor, 1);

    in this post, but still after alt+f2 ‘r’ the dock is as first element in _leftBox. also tried other numbers to no avail, so i assume it must be something else with the dock extension.

    heres the modified dock extension code if someone is interested:
    http://sprunge.us/TJSc
    http://sprunge.us/BLag
    (also added not running icons to be grayscale, but yeah, doesnt “work” with grayscale icons:)

    ps. and yeah, i havent asked upstream cause i have the feeling that what i’m doing is “blasphemy” and wanted to avoid any politics…

  • This is great! I didn’t know there was this much capability it Gnome Shell extensions. Might it be possible to create an extension to add a taskbar? I found a solution online using a program called tint2, but it would be nice to have one built for Gnome. I presume that would be difficult, but it could have a notification area to replace the built in one and things like that. Thanks for a great post!

  • Ufatam

    Keep up the good work. I agree with you totally and i do hope the end user is kept in mind as they plan to release Gnome 3.2 by the end of the year.

  • Lumak

    Thankyou for your very insightfull posts into modding the gnome shell. I gives me so many ideas like creating a desktop activity, or an XMMS2 activity with player box at the bottom of the overview, But I’m still at a loss, even doing somthing simple I continue to fail at.

    Currently I’m looking for a way to add a window close button to the panel next to the app menu. In contrast to the “Quit” option that closes the instance of the application, I want a close window button for full screen applications (of which I remove the title bar from with a custom style). The reasoning being that something like Evolution opens message windows as children so the App menu (Quit) will close evolution completely.

    I’ve tried hacking apart the AppMenuButton class and turning it into an AppCloseButton (one that would display no menu but simply close the currently focused window when pressed.) Perhaps it’s because I’m tired that I can’t figure it out right now.

  • Lumak

    Also in prelude to me attempting to install gnome-shell on my tablet computer, I would also like to work out making a HotSpot dock for my input method of choice e.g. QWO or Cellwriter. Interesting to note that your screenshots show a keyboard icon in the system status, Does that actually display caribou? You must be running a newer shell than me?

  • Excellent article as usual.

    Do you have any suggestions in regards to removing the name from the status user menu? Now that I have app indicators again I’d like to reclaim some of that space.

  • Börje Holmberg

    Very nice info.

    I find it hard to live without the mail-notification on the top panel. How would you set up /usr/bin/mail-notification so that it will run and advertise of incoming mails? It really does not matter where it is placed, main thing is that it hollers and shows the number of new mails.

    I have succeeded in putting it on the panel, but it does not run. It is just a dead icon.

  • Raul

    Nice Article!

    In Ubuntu, I get an error with Example 6 (menu)

    appInfo.get_section() is undefined

    And doesn’t work. Any idea?

  • Raul,
    I too am using Ubuntu. Is the Gnome Shell in the latest version? Mine would not update, so I went into the Ubuntu Software Center, uninstalled the gnome shell, and then installed it again. This got me to the latest version.

  • Felipe

    I’d like to implement hte example 2 but i dont now how, which file should I create or edit? and how to run it?

    thanks

  • Raul

    I used the ppa, updated every day :-D

  • Felipe

    i got an error with example 6 too. I’m using ubuntu 11.04 and i used the ppa too.

    i dont know the error but the icon doesnt appear on the top bar

  • Andy

    Excellent post – thanks!

  • Wompatti

    Great work. I tried the gnote to tray extension, and got it working with diodon, which is now in the top panel. How do I make it work with Empathy and tomboy-notes? I tried making a new line to the extension.js file, and only replaced ‘diodon’ with ‘tomboy-notes’, but Tomboy didn’t move to the top panel. Should I make another copy of the extension if I want another icon to the top panel, or what?

  • Perfect, thank you.
    But taking advantage of, would have any possibility to disable the notification area for empathy? is very annoying and gets in the way too.
    Many thanks!!!

    sorry for my bad english

  • tim

    Excellent article. Thanks.

    I’m not surprised that you observe this:
    “Sometimes I get the impression that the GNOME Shell was designed and put together by a group of arrogant young software engineers who were more interested in adhering to the tenants of so-called usability and design theory studies than in what their end users really needed in a modern graphical shell”

    …because the GNOME 2 developers gave me the same impression, all those years ago. I bet they all drive cars with automatic gearboxes.

    There is a UI design idea to provide three levels of user – beginner, normal user, and expert. Let people customise whatever they want, when they feel comfortable. Just because I _can_ write a few hundred lines of JavaScript, does not mean that I want to do this in order to move a menu, or change a font somewhere.
    The Gnome folks must have ignored that piece of UI design, probably because there was no super-complicated paper written about it.

    -tim

  • Just popped in to thank for your visit to Webupd8 the other day. Your assistance in showing me how to remove the user name being displayed in the status menu is greatly appreciated personally & I know others benefited from all your comments as well.

  • Rémi

    Great post ! I think I will start running these examples and maybe eventually develop extensions on my own.

    But I have a question regarding internationalization : what is the best way to do it ? In the example, the gettext domain is gnome-shell, so I believe that the translation is in /usr/share/locale/xx/LC_MESSAGE/gnome-shell.mo… But as extensions are often installed in the user’s home directory, perhaps the .mo file should be there too…

    Any ideas/directions on this topic ?

    Rémi

    • @ Remi, have a look how I implemented message catalog support in the helloworld1 extension which you can download from http://fpmurphy.com/gnome-shell-extensions. I too believe that message catalogs for extensions should reside under the extension directory and plan to publish a draft specification to that effort in the near future.

  • Morning, been messing with your eureka v2.1 extension, real slick, but I noticed that the icons are huge when used with anything other than the Adwaita shell theme. Have you encountered the same thing? I tried it on my main system & a fresh installation.

    It’s really nice though, let’s me trim at least 4 other extensions.

  • Vallimar

    Here is a diff I made that will allow you to easily replace the (IMO redundant) Activities button with the applications button.
    Used the code and examples to cobble the changes.

    — extension.js 2011-05-15 14:38:48.000000000 -0400
    +++ new-extension.js 2011-05-27 18:28:07.830337970 -0400
    @@ -47,10 +47,16 @@
    _init: function(mode) {
    PanelMenu.Button.prototype._init.call(this, 0.0);

    – this._icon = new St.Icon({ icon_name: ‘fedora-logo-icon’,
    – icon_type: St.IconType.FULLCOLOR,
    – icon_size: Main.panel.button.height });
    – this.actor.set_child(this._icon);
    + this._box = new St.BoxLayout({ style_class: ‘activities_box’});
    + this._label = new St.Label({ text: _(” Apps”),
    + style_class: ‘activities_text’ });
    + this._icon = new St.Icon({ icon_type: St.IconType.FULLCOLOR,
    + icon_size: Main.panel.button.height,
    + icon_name: ‘fedora-logo-icon’ });
    +
    + this._box.add_actor(this._icon);
    + this._box.add_actor(this._label);
    + this.actor.set_child(this._box);

    this._appSys = Shell.AppSystem.get_default();
    this._categories = this._appSys.get_sections();
    @@ -62,8 +68,8 @@
    Main.queueDeferredWork(this._reDisplay);
    }));

    – // add immediately after hotspot
    – Main.panel._leftBox.insert_actor(this.actor, 1);
    + // add immediately before hotspot
    + Main.panel._leftBox.insert_actor(this.actor, 0);
    Main.panel._menus.addMenu(this.menu);
    },

    @@ -110,6 +116,8 @@
    Gettext.bindtextdomain(“applications_button”, userExtensionLocalePath);
    Gettext.textdomain(“applications_button”);

    + Main.panel.button.visible = false;
    +
    new ApplicationsButton(true);
    }

  • maverick280857

    I’ve been trying to figure out how to promote the wicd icon from the bottom notification section to the top bar. This is the relevant line in extension.js for App@Indicator:

    StatusIconDispatcher.STANDARD_TRAY_ICON_IMPLEMENTATIONS[‘wicd-client’] = ‘wicd-client’;

    I know that wicd-client is what executes the wicd gtk frontend, but this doesn’t work for me, I have also tried other variations. Do you happen to know how one can figure out the correct string that has to be put here, if the text displayed when the mouse hovers over the notification section icon is a descriptive string and not the command?

  • maverick280857

    Okay I found a solution to this. Use

    StatusIconDispatcher.STANDARD_TRAY_ICON_IMPLEMENTATIONS[‘wicd-client.py’] = ‘wicd-client.py’;

  • SB

    Great post!
    In example 6 I got an error. Fixed it by replacing the below code:
    088 this.appItems[appInfo.get_section()].menu.addMenuItem(appName);
    089 appName.connect(‘activate’, function(actor,event) {
    090 let id = actor._appInfo.get_id();
    091 Shell.AppSystem.get_default().get_app(id).activate(-1);
    092 });

    with:

    if (appInfo.get_section() != null) {
    this.appItems[appInfo.get_section()].menu.addMenuItem(appName);
    appName.connect(‘activate’, function(actor,event) {
    let id = actor._appInfo.get_id();
    Shell.AppSystem.get_default().get_app(id).activate(-1);
    });
    }

    Hope that helps someone.

  • Dan

    Good evening, good sir.

    I chatted with Rahul in the batcave about this, and he suggested I talk to you.

    What I’d like to ask is if it would be possible to duplicate the functioning of the left upper “hot corner”, in the right upper corner also? The goal, is to get a “hot” activation corner closer to the workspaces selector.

    My own coding skills are nill and none, so I’m at your mercy here.

    Thanks for reading this, and I hope it can be easily done.

    Dan

    • @Dan. Yes, it can be done. Sounds like you have a workplace selector extension installed on the right hand side of your top panel in the status tray. I have seen such an extension. Is this correct?

  • Dan

    There is one available, but I do not have it installed. Essentially, I like the behavior of the workspace selector as it is supplied with the default install, I simply wish to be able to “bring it up” (along with the overlay) from the upper right corner, as well as the upper left. I suspect this would alleviate quite a bit of discomfort with folks who are trying to get to know and love Gnome-Shell. I see, and can sympathize with a lot of comments on the forum about having to go from one side of the screen all the way to the other with the mouse. Admittedly, the [ALT] key already simplifies this, but to the keyboard-challenged, it’s a bit of a negative.

    • @Dan. You have a rather strange email address :-). OK, try out the righthotcorner extension which I have placed in
      fpmurphy.com/gnome-shell-extensions and let me know if this meets your needs.

  • Charles Bowman

    Since we’re making a wishlist. :) I’d like to move the hotspot to the upper right corner, doesn’t need an icon or text. It just has to activate.

  • Philip Pirrip

    I’m just curious, and you seem to have a lot of knowledge to tell me that: would it be possible to have extensions that will give us back the options to have, e.g. wobbly windows, or to change the opacity of a window (that comes in so handy sometimes), or even drag a window to another workspace.
    I mean, is it possible to operate on windows with all this javascript?
    (btw – great blog, I enjoy reading it)

    • @Philip. Yes what you are looking for is entirely possible but would require a lot of modifications to mutter. Raise enhancement requires in GNOME Bugzilla if this is something you really want.

  • I’m loving that righthotcorner extension, I tweaked it a bit to better suit my habits and it’s perfect.

    Thank you Mr. Murphy.

    • The “landing zone” is only one pixel wide in the extension. This was was done to match the official hot corner landing zone. This can sometimes be difficult to hit. One way to improve the hit rate is to increase the size of Clutter.Group and Clutter.Rectangle. 5px is a suitable value.

  • Thank you for this very interesting article!

    The one thing I still miss badly at Gnome 3 is a way to switch workspaces just by moving the mouse to the screen edges. Usually it’s the left and right edge, but in Gnome 3 it would be the top and bottom edge, I guess. Is it possible to implement this by Gnome Extensions as well?

  • ZTomkoski

    It will be interesting to see what functionally hacking away at the Clutter manager will bring as Gnome 3 gets into more distros. I was looking at the code used in compiz for Gnome 2.x to try to start figuring out how to return wobbly windows. The code for it was well into hundreds of lines of coding to give the effect. It will probably be a while before anything like wobbly windows comes out.

  • Dave Hampton

    in your weather app… putting in a city name for the ZIPCODE const, is convenient and displays better than the zip. After all, when I click the button, I want to see the city, not the zip.

    However, when you put in a city, the display baloons to “Los Angeles, United States of America” – which is a bit wordy…

    So in the _displayUI function, i recommend changing the currLocation variable to :
    let currLocation = request[0].query.split(‘,’)[0];

    which just grabs everything prior to the first comma.

    Thanks and great extension!

    dave

  • Jutt

    Loving these customizations. Just one question: I’m using the noim extention and wanted to change the icon, the extension.js stated to place icon in “appropriate directory” but which directory is that? Sorry if this is damn obvious…

  • Jutt

    Sorry ignore my previous question, figured it out (as is usually the way when I dare to ask)

    Although I do have another question (sorry) I’ve been trying to figure out a way of removing the right hand side of the calendar (the events/evolution bit) any advice?

  • Josh

    Hello,
    It seems like the weather extension doesn’t load up when I am behind a proxy. Is it possible to specify proxy server and authentication details somewhere in the extension so that it can connect to internet?

    I already have proxy specified in Gnome settings.

    • @Josh. Yes, that is probably true. You should modify the weather data acquisition URL to take account of your particular proxy.

  • Amir

    Hi, So amazed recently I wrote my own replacement of netspeed for gnome 3,
    I am looking for more polishing and code style fixing as well. I was looking for a place to
    publish it and get feed-back or possible improvement also would love to get more extensions
    from other people.

    Some market, or repository something like firefox has, would be great for gnome 3.2, this is it feature I am looking forward to see in gnome 3.2
    Some utility to get extensions for gnome 3, also popular applications may be written, this totally opened my eyes
    to what application development for feature desktop may look like.

    So is there any one I can talk about this? I am not very familiar with the gnome community! But I may be able to write some code (not too much, I am a busy student) :(

  • Charles Bowman

    Just tried your autohide extension, it works brilliantly, very nice job. ;-)

  • Jasper St. Pierre

    > For example, why is the GNOME shell dependant on two distinct JavaScript engines, i.e. gjs and seed (webkit)?

    It’s not. It’s dependent on one: gjs.

    > const Gtk = imports.gi.versions.Gtk = ‘3.0’;

    This is just wrong. These need to be two separate statements:

    imports.gi.versions.Gtk = ‘3.0’;
    const Gtk = imports.gi.Gtk;

    Notice how the first line has “gi.versions.Gtk”, while the second has “gi.Gtk”. With your version, you will get a Gtk object that’s the literal string “3.0”.

    Additionally, don’t encourage people to do this in their extensions. We already do this in the shell itself.

    http://git.gnome.org/browse/gnome-shell/tree/js/ui/environment.js

  • Can the weather add on be updated to allow names? I can’t add my local town I have to choose the major one 20 minutes away?
    Also can it be tweaked to add to the status the weather trend, or the ability to show more without having to click and open it?

    Thanks!

    • @David. Sure it can be modified to do what you want. Not a major deal.

      You may have to use a different service to get the weather for your particular town. If you let me know the name of the town, I will see what I can do.

  • maverick280857

    Is it possible to configure the Gnome Shell UI to not use Network Manager at all, in the status indication?

    I am unable to log into the Gnome Shell the first time I try (with or without extensions). I get the error message ‘something bad happened…’ and am forced to log out. When I log back in, everything seems to work fine. I compared ~/.xsession-errors for the failed and successful login sessions and I found the following chunk of errors in the log file for the failed login:

    JS ERROR: !!! Exception was: Error: Requiring NetworkManager, version none: Typelib file for namespace ‘NetworkManager’ (any version) not found
    JS ERROR: !!! lineNumber = ‘0’
    JS ERROR: !!! fileName = ‘gjs_throw’
    JS ERROR: !!! stack = ‘(“Requiring NetworkManager, version none: Typelib file for namespace ‘NetworkManager’ (any version) not found”)@gjs_throw:0
    @/usr/share/gnome-shell/js/ui/status/network.js:8

    JS ERROR: !!! message = ‘Requiring NetworkManager, version none: Typelib file for namespace ‘NetworkManager’ (any version) not found’
    JS LOG: NMApplet is not supported. It is possible that your NetworkManager version is too old

    I don’t have any network-manager packages installed. Please also see http://ubuntuforums.org/showpost.php?p=10923040&postcount=234.

  • kio

    Hi! How can disable the fade effect when open a folder in nautilus? It’s very bad.

    • @KIo. I do not get such an effect when opeing a folder in Nautilus so have no idea what you are talking about. Sorry

  • Josh

    Thanks for pointers fpmurphy!

    Do you have an idea how should the URL be modified in this case?
    I have a proxy server: proxyserver:port and username:password.

    Haven’t done much of JavaScript :(

    Many thanks in advance.

    /Josh

  • Hi, I installed the applicationbutton extension, but it doesn’t start. In the Gnome Tweak Tool, Shell Extension tab, the “Application Menu Button Extension” on/off button is greyed out, not clickable. Do you have any ideas?

    Thanks

    • @Marcello. What error message are you seeing in the errors section of Looking Glass? Reload the GNOME Shell and straight away access LG using Alt-F2 lg.

      • Perhaps, I’ve the same problem…

        In “lg” screen I’ve this error :

        While loading extension from “/usr/share/gnome-shell/extensions/applicationsbutton@fpmurphy.com
        Warning: Missing “url” property in metadata.json
        While loading extension from “/usr/share/gnome-shell/extensions/applicationsbutton@fpmurphy.com
        Failed to evaluate main function:TypeError: this.appItems[appInfo.get_section()] is undefined

        • OK found it using code from your own extension eureka…

          You have to correct this :

          this.appItems[appInfo.get_section()].menu.addMenuItem(appName);
          appName.connect(‘activate’, function(actor, event) {
          let id = actor._appInfo.get_id();
          Shell.AppSystem.get_default().get_app(id).activate(-1);
          });

          by this :

          let sec=appInfo.get_section();
          if (sec) {
          this.appItems[sec].menu.addMenuItem(appName);
          appName.connect(‘activate’, function(actor, event) {
          let id = actor._appInfo.get_id();
          Shell.AppSystem.get_default().get_app(id).activate(-1);
          });
          }

          in extension.js

  • steven0lisau

    Thank you for your code!

    Do you have a better way to debug your code without restarting gnome-shell?

    One more thing, mind you telling me how to save my code? I lost my customized code after updating my gnome.

  • Jim (SUSE 11.4)

    Installed ‘Applications Menu’ (ex 6), but,can’t get to work, ran looking glass, and corrected a few syntax errors, but still no joy, now getting an error, ‘Failed to evaluate main function’: 0 is not defined’. I modified the script as per SB’s post, but still the same error. Help would be appreciated.

    Cheers

  • Scott

    Thank you very much for this site! I was getting ready to abandon Gnome and move to KDE, your code has been a lifesaver.

  • knut

    Hey, thanks for your great post!
    I’m using a lot of extensions and changed everything a bit – but one thing I cannot do is rearrange the extension icons in the panel.

    E.g. your places extension I have moved from center to leftBox, but now it appears right next to the application menu, which is kind of ugly, because depending on the length of applications name it’s moving arround. I want it on the right side of the hot button, how can i do that?

    On the other side of the panel i have a music player extension, which’s button appears in the right corner, i want this button to be next to the other system status icons.

    Perhaps you can tell me how to rearrange icons in the panel?

    Thanks a lot,
    knut

  • Rob

    First thanks for all this, having fled Unity and being dragged kicking and screaming into the brave new world gnome-shell is becoming a pleasure, not a nightmare, pretty much entirely thanks to your work here.
    I tried the Eureka 2.1 extension where the A11y icon disappeared OK and the ‘Activities’ changed to button but no appearance of the integrated apps menu. I then deleted it and dropped back to 2.0 (as I think I prefer to keep the ‘Activities’ text) but the integrated menu still doesn’t appear. If I refresh shell, fire up lg and look at errors I see “Eureka- Failed to evaluate main function: Error Argument ‘actor’ (type Interface) may not be null”
    Are there any kind souls here with some idea how to fix please?

  • Xueqian

    First, thanks.
    I tried the extension that moves the message icon trays to the top-panel. However, I met a problem that if I ran several apps with all their icon on the top-panel, all the apps show the same icon. If one changed, others changed as well. What can I do such that each app will show its own icon?

  • Enrico

    Hi FPMurphy,

    Thanks for all the great extensions. I have implemented a number of them, and I am now a happy camper except for the “hot corner”.

    I am not too familiar with Java etc, so writing or trying to modify existing extensions is a task I’d rather not attempt without guidance just incase I stuff up my system big time.

    I love the idea of the “hot corner” in g-s, but would like to enter a slight delay of say 500ms or so to prevent it from activating if I move the mouse into the corner by mistake, especially when working with applications and moving to a menu option that exists in the top left corner. Is this possible, and if so, how would I go about this?

    Thanks
    Enrico

  • sergeant

    Hi Finnbarr,

    Thanks for the such detailed explanation and extensions you’ve developed.

    Could you help to create an extension to close the active application from within top bar.
    The reason I am asking is that I would like to remove title bar from maximized windows which works wonderful except it makes slightly more difficult to close applications.

    I am looking into two options for closing:

    1) Middle click on top bar (icon or app name)
    2) A dedicated “X” button to the left of app name

    What kind of events I should bind to?
    How can I send a signal to the active application?

    Thanks a lot!

    • Hi Sargent,

      Maybe I am missing something but you can already close the active application from the top bar simply by right clicking on the top bar icon and selecting the Quit option.

      • sergeant

        True, but it requires two clicks instead of one.
        At the end of a working day I have to close a dozen of apps before shutting down my PC so it’s 24 clicks instead of 12, big difference.