更多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包括:
以上提到的java脚本代码都在目录/usr/share/gnome-shell下。
预览窗口是大家使用‘Win徽标键’/点击‘活动’出看到的窗口。代码主要是在../js/ui/overview.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 < 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 < 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(); }
注意,也可以在‘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 < 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(); }
创建图标完成后,可以设置图标大小的参数,位于../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 < 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(); }
『示例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 < 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 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); }
『示例7』
话说回来,不是所有的GNOME Shell组件都可以那么轻易的修改或者个性化设置。
示例7就是显示搜索引擎wiki和google的图标。
这里我不再给大家翻译了。
『结论』
我强烈建议大家看看原文。原文地址@ here。因缺乏相关只是,我翻译的地方可能不太好理解。大家还是自己看看源文章。我只是给大家提供一个总体思路吧。只要大家能够随心所欲配置自己的GNOME 3,笨兔兔的目的就达到啦

浙公网安备 33010602011771号