GNOME Shell 3.38.1中如何覆写仅含_init()的SystemBackground类函数
针对你在GNOME Shell 3.38.1下覆写仅含_init()方法的SystemBackground类的需求,这里有两种可靠的实现方式,核心思路是通过猴子补丁(Monkey-patching)或者重写类定义来替换目标颜色值,同时保证原有功能不受影响:
方法1:猴子补丁覆写_init方法(推荐)
这种方式不需要完全重写整个类,只需拦截原类的_init方法,在合适的时机修改背景颜色,既能保留原有逻辑,又能实现自定义需求:
const { Clutter } = imports.gi; const backgroundModule = imports.ui.background; let originalInit; function enable() { // 保存原有的_init方法引用 originalInit = backgroundModule.SystemBackground.prototype._init; // 覆写_init方法 backgroundModule.SystemBackground.prototype._init = function() { // 先执行原_init逻辑,确保基础功能正常运行 originalInit.call(this); // 修改新创建实例的背景颜色 if (this.content && this.content.background) { const customColor = Clutter.Color.from_string('#00ff00')[1]; this.content.background.set_color(customColor); } // 同步更新全局单例的颜色(避免后续复用旧颜色) if (backgroundModule._systemBackground) { const customColor = Clutter.Color.from_string('#00ff00')[1]; backgroundModule._systemBackground.set_color(customColor); } }; // 对已经存在的SystemBackground实例生效 updateExistingBackground(); } function disable() { // 恢复原有的_init方法 backgroundModule.SystemBackground.prototype._init = originalInit; // 恢复默认背景颜色 const defaultColor = backgroundModule.DEFAULT_BACKGROUND_COLOR; if (backgroundModule._systemBackground) { backgroundModule._systemBackground.set_color(defaultColor); } updateExistingBackground(defaultColor); } function updateExistingBackground(color = null) { // 遍历全局窗口Actor,找到SystemBackground实例并更新颜色 global.get_window_actors().forEach(actor => { if (actor instanceof backgroundModule.SystemBackground) { const targetColor = color || Clutter.Color.from_string('#00ff00')[1]; actor.content.background.set_color(targetColor); } }); }
为什么这个方法可行?
- 它保留了原类的所有核心逻辑(比如
loaded信号、全局_systemBackground单例机制),仅修改我们关心的颜色部分 - 同时覆盖了已存在的实例和新创建的实例,确保所有场景下颜色都能正确生效
方法2:完全重写SystemBackground类
如果你需要更大的自由度(比如修改_init中的其他逻辑),可以直接替换原模块中的SystemBackground类定义:
const { GObject, Meta, Clutter, GLib, global } = imports.gi; const backgroundModule = imports.ui.background; let originalSystemBackground; let originalSystemBackgroundInstance; function enable() { // 保存原类和已存在的实例引用 originalSystemBackground = backgroundModule.SystemBackground; originalSystemBackgroundInstance = backgroundModule._systemBackground; // 重新注册SystemBackground类,替换原定义 backgroundModule.SystemBackground = GObject.registerClass({ Signals: { 'loaded': {} }, }, class SystemBackground extends Meta.BackgroundActor { _init() { if (backgroundModule._systemBackground == null) { backgroundModule._systemBackground = new Meta.Background({ meta_display: global.display }); // 直接设置自定义颜色 const customColor = Clutter.Color.from_string('#00ff00')[1]; backgroundModule._systemBackground.set_color(customColor); } super._init({ meta_display: global.display, monitor: 0, }); this.content.background = backgroundModule._systemBackground; let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, () => { this.emit('loaded'); return GLib.SOURCE_REMOVE; }); GLib.Source.set_name_by_id(id, '[gnome-shell] SystemBackground.loaded'); } }); // 更新已存在的实例颜色 if (originalSystemBackgroundInstance) { const customColor = Clutter.Color.from_string('#00ff00')[1]; originalSystemBackgroundInstance.set_color(customColor); updateExistingBackground(); } } function disable() { // 恢复原类定义 backgroundModule.SystemBackground = originalSystemBackground; // 恢复默认颜色和全局单例 if (originalSystemBackgroundInstance) { originalSystemBackgroundInstance.set_color(backgroundModule.DEFAULT_BACKGROUND_COLOR); updateExistingBackground(backgroundModule.DEFAULT_BACKGROUND_COLOR); } backgroundModule._systemBackground = originalSystemBackgroundInstance; } function updateExistingBackground(color = null) { global.get_window_actors().forEach(actor => { if (actor instanceof originalSystemBackground) { const targetColor = color || Clutter.Color.from_string('#00ff00')[1]; actor.content.background.set_color(targetColor); } }); }
注意事项:
- 重写类时要完全复制原类的逻辑(包括信号定义、单例处理、
loaded信号发射),避免破坏GNOME Shell原有功能 - 在
disable方法中一定要恢复原类和实例,否则扩展禁用后会导致Shell异常
关键细节说明
Clutter.Color.from_string的返回值:这个方法返回一个数组,第一个元素是布尔值(表示颜色解析是否成功),第二个元素是实际的Clutter.Color实例,所以我们需要取[1]来获取可用的颜色对象。- 全局单例
_systemBackground:原类使用了全局单例复用背景实例,所以不管是覆写_init还是重写类,都要确保这个单例的颜色被正确修改,否则新创建的SystemBackground会复用旧的颜色值。 - 已存在实例的更新:仅修改类定义不会影响已经创建的实例,所以需要额外遍历全局窗口Actor,找到已有的
SystemBackground实例并更新其颜色。
内容的提问来源于stack exchange,提问作者PRATAP-PANABAKA
相关产品推荐
相关产品推荐

