更多GNOME Shell个性化配置(转)

GNOME shell本质上来说,是窗口管理器、应用启动器、桌面布局的集合。大多数图形代码用JavaScript写成。

下图是GNOME Shell 3.0版本技术的一个示意图:

【上图是GNOME官网图表的改进】

GNOME shell使用OpenGL来渲染图形。OpenGL使用硬件加速像素格式,同时支持软件渲染。然而,运行GNOME Shell需要启动硬件加速,GNOME shell运行也需要强大的3D加速能力。我们用的近3年内的显卡一般都支持硬件加速。若你的硬件加速启用不了,则GNOME Shell默认返回GNOEM 2面板模式。要想察看你的显卡是否支持硬件加速,可利用/usr/libexec/gnome-session-is-accelerated下的helper程序察看参数值0还是1:

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;
}

如果你确信你的显卡支持硬件加速,但因某些原因GNOME shell只能运行在fallback模式,那么你可以尝试使用返回ture的脚本替代该程序。

窗口管理Mutter是改进后的Metacity。在GNOME 2.2引进Metacity前,GNOME 使用过Enlightenment,后来又使用Sawfish作为它的窗口管理器。Metacity使用GTK+图形部件工具来创建用户界面组件,这样就可以变更Metacity显示主题,也可以与其他GTK+程序协调搭配。Mutter是基于Metacity和Clutter的、新的窗口管理器。提示,GNOME Shell fallback模式仍然使用Metacity和GTK+。当然,正常可以硬件加速的同学就可以使用Mutter窗口管理器了。
GOblect Introspection层在Mutter和Shell套件之上。一方面来看,GOblect Introspection层是位于Mutter、Shell套件库、JavaScript间的粘着层。GOblect Introspection用来自动组合GNOME Shell图形界面。同时,Javascript真正使用的版本是1.8.5.
GOblect Introspection的目标是为了描述可以以机器可读XML格式的GIR式应用程序接口。其中,typelib是GIR的综合版本,速度快、合理利用内存,同时也可以让其他语言完全写入,不需其他信息资源。大家可以使用g-ir-generate来测试一个指定的typelib内容。比如,下面的例子就是一个st_texture_cache_load_uri_sync的typelib文件:

#  g-ir-generate /usr/lib64/gnome-shell/St-1.0.typelib
....
 
          <span style="color: #008000;"></span>
 
....

各位同学如果想要成功的个性化设置GNOME Shell,最好是要理解GNOME Shell 图形界面的各种组成部分才行。下面是GNOME Shell屏幕的各个部分示意图:

由上图,咱们可以看出GNOME Shell包括

  • *顶部面板(或顶部栏)(../js/ui/panel.js)
  • *活动按钮(也就是热键区域),可以预览窗口(../js/ui/panel.js)
  • *应用程序菜单,现实目前正在打开的应用程序名称(../js/ui/panel.js)
  • *时间,同时包括日历显示(../js/ui/dateMenu.js)
  • *系统状态图标,包括诸如系统通用性设置、蓝牙、键盘布局、输入法等(../js/ui/status/*)
  • *状态菜单,包括用户相关选项。我们可以在此改变IM状态,个人设置以及退出登陆(../js/ui/statusMenu.js)
  • *提示区域,屏幕底部栏,可自动隐藏(../js/ui/notificationDaemon.js)
  • *消息托盘,这里现实提示消息和其他的信息(../js/ui/messageTray.js)
  • 以上提到的java脚本代码都在目录/usr/share/gnome-shell下。

    预览窗口是大家使用‘Win徽标键’/点击‘活动’出看到的窗口。代码主要是在../js/ui/overview.js。下面是预览窗口的组成部分介绍:

    从上图,我们可以看出预览窗口的组成部分:

  • *面板,左侧的垂直条,可以显示大家收藏的应用(../js/ui/dash.js)
  • *视图选择器,可以在窗口/应用程序间切换(../js/ui/viewSelector.js, ../js/ui/overview.js)
  • *搜索框(搜索框../js/ui/viewSelector.js , 搜索结果 ../js/ui/searchDisplay.js)
  • *工作分区,右侧的垂直条,有当前所在工作分区的应用缩略显示(../js/ui/workspaceSwitcherPopup.js)
  • 范例讲解

    下面就是具体的个性化设置GNOEM Shell例子,既可以通过修改源代码也可以使用扩展来进行。

    『示例1』

    GNOME shell开发者们一直在想剔除顶部面板上的‘传统消息提示区域’。但是,托盘图标仍在系统状态的一侧。
    例如,打开gnote,就显示在GNOME Shell信息区域。

    我们可以通过下面一个简单的扩展让gnote显示在顶部面板
    扩展代码

    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';
     
    }

    应用扩展后效果如下图:

    提示,请使用imports来导入一个模块,例如:

    const Gtk = imports.gi.versions.Gtk = '3.0';
    『示例2』

    已经有个叫alternative status menu的gnome shell扩展,可以在状态菜单中显示关机、休眠、睡眠状态,而不是仅仅显示单个‘休眠’状态。
    如图,

    但是,我们还可以通过修改源代码来显示其他状态,达到上图同样效果:

    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 &lt; 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);
     
    }

    电源休眠模式很多种,上述例子采用默认模式。

    『示例三』

    该例子为大家展示下如何在顶部面板中间添加菜单,如下图所示:

    下面是创建菜单的扩展源代码:

    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 &lt; 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 &lt; 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 &gt; 0) {
                this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
            }
     
            // Display default mounts
            for ( let mountid = 0; mountid &lt; 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();
    }

    注意,也可以在‘Main.palcesManager’中恢复‘所有位置’、‘书签’、‘挂载’:

    places = Main.placesManager.getDefaultPlaces(); bookmarks = Main.placesManager.getBookmarks(); mounts = Main.placesManager.getMounts();
    『示例4』

    下面给大家展示下如何恢复『示例3』中菜单的图标,如下图所示:

    源码如下:

    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 &lt; 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 &lt; 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 &gt; 0) {
               this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
           }
     
            // Display default mounts
            for ( let mountid = 0; mountid &lt; 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();
    }

    创建图标完成后,可以设置图标大小的参数,位于../js/ui/placeDisplay.js

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

    下面的例子给大家展示下改进前面的例子:将图标放在标签名称前面,如图所示:

    代码如下:

    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 &lt; 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 &lt; 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 &gt; 0) {
               this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem());
           }
     
            // Display default mounts
            for ( let mountid = 0; mountid &lt; 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();
    }
    『示例6』

    该例子展示如何紧靠‘Activities按钮’(活动)来添加如下所示:

    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 &lt; 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 &gt;= 0; appid--) {
                let appInfo = appInfos[appid];
                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 &lt; 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);
    }
    『示例7』

    话说回来,不是所有的GNOME Shell组件都可以那么轻易的修改或者个性化设置。
    示例7就是显示搜索引擎wiki和google的图标。

    这里我不再给大家翻译了。

    『结论』

    我强烈建议大家看看原文。原文地址@ here。因缺乏相关只是,我翻译的地方可能不太好理解。大家还是自己看看源文章。我只是给大家提供一个总体思路吧。只要大家能够随心所欲配置自己的GNOME 3,笨兔兔的目的就达到啦

    posted @ 2011-05-26 17:49  御剑逍遥  阅读(908)  评论(0)    收藏  举报