// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- const Config = imports.misc.config; imports.gi.versions.Clutter = Config.LIBMUTTER_API_VERSION; imports.gi.versions.Gio = '2.0'; imports.gi.versions.GdkPixbuf = '2.0'; imports.gi.versions.Gtk = '3.0'; imports.gi.versions.TelepathyGLib = '0.12'; imports.gi.versions.TelepathyLogger = '0.2'; const { Clutter, GLib, Shell, St } = imports.gi; const Gettext = imports.gettext; // We can't import shell JS modules yet, because they may have // variable initializations, etc, that depend on init() already having // been run. // "monkey patch" in some varargs ClutterContainer methods; we need // to do this per-container class since there is no representation // of interfaces in Javascript function _patchContainerClass(containerClass) { // This one is a straightforward mapping of the C method containerClass.prototype.child_set = function(actor, props) { let meta = this.get_child_meta(actor); for (let prop in props) meta[prop] = props[prop]; }; // clutter_container_add() actually is a an add-many-actors // method. We conveniently, but somewhat dubiously, take the // this opportunity to make it do something more useful. containerClass.prototype.add = function(actor, props) { this.add_actor(actor); if (props) this.child_set(actor, props); }; } function _patchLayoutClass(layoutClass, styleProps) { if (styleProps) layoutClass.prototype.hookup_style = function(container) { container.connect('style-changed', () => { let node = container.get_theme_node(); for (let prop in styleProps) { let [found, length] = node.lookup_length(styleProps[prop], false); if (found) this[prop] = length; } }); }; layoutClass.prototype.child_set = function(actor, props) { let meta = this.get_child_meta(actor.get_parent(), actor); for (let prop in props) meta[prop] = props[prop]; }; } function _adjustEasingTime(msecs) { if (!St.Settings.get().enable_animations) return 1; return St.get_slow_down_factor() * msecs; } function _loggingFunc() { let fields = {'MESSAGE': [].join.call(arguments, ', ')}; let domain = "GNOME Shell"; // If the caller is an extension, add it as metadata let extension = imports.misc.extensionUtils.getCurrentExtension(); if (extension != null) { domain = extension.metadata.name; fields['GNOME_SHELL_EXTENSION_UUID'] = extension.uuid; fields['GNOME_SHELL_EXTENSION_NAME'] = extension.metadata.name; } GLib.log_structured(domain, GLib.LogLevelFlags.LEVEL_MESSAGE, fields); } function init() { // Add some bindings to the global JS namespace; (gjs keeps the web // browser convention of having that namespace be called 'window'.) window.global = Shell.Global.get(); window.log = _loggingFunc; window._ = Gettext.gettext; window.C_ = Gettext.pgettext; window.ngettext = Gettext.ngettext; window.N_ = s => s; // Miscellaneous monkeypatching _patchContainerClass(St.BoxLayout); _patchLayoutClass(Clutter.TableLayout, { row_spacing: 'spacing-rows', column_spacing: 'spacing-columns' }); _patchLayoutClass(Clutter.GridLayout, { row_spacing: 'spacing-rows', column_spacing: 'spacing-columns' }); _patchLayoutClass(Clutter.BoxLayout, { spacing: 'spacing' }); let origSetEasingDuration = Clutter.Actor.prototype.set_easing_duration; Clutter.Actor.prototype.set_easing_duration = function(msecs) { origSetEasingDuration.call(this, _adjustEasingTime(msecs)); }; let origSetEasingDelay = Clutter.Actor.prototype.set_easing_delay; Clutter.Actor.prototype.set_easing_delay = function(msecs) { origSetEasingDelay.call(this, _adjustEasingTime(msecs)); }; Clutter.Actor.prototype.toString = function() { return St.describe_actor(this); }; // Deprecation warning for former JS classes turned into an actor subclass Object.defineProperty(Clutter.Actor.prototype, 'actor', { get() { let klass = this.constructor.name; let { stack } = new Error(); log(`Usage of object.actor is deprecated for ${klass}\n${stack}`); return this; } }); let origToString = Object.prototype.toString; Object.prototype.toString = function() { let base = origToString.call(this); try { if ('actor' in this && this.actor instanceof Clutter.Actor) return base.replace(/\]$/, ' delegate for ' + this.actor.toString().substring(1)); else return base; } catch(e) { return base; } }; // Work around https://bugzilla.mozilla.org/show_bug.cgi?id=508783 Date.prototype.toLocaleFormat = function(format) { return Shell.util_format_date(format, this.getTime()); }; let slowdownEnv = GLib.getenv('GNOME_SHELL_SLOWDOWN_FACTOR'); if (slowdownEnv) { let factor = parseFloat(slowdownEnv); if (!isNaN(factor) && factor > 0.0) St.set_slow_down_factor(factor); } // OK, now things are initialized enough that we can import shell JS const Format = imports.format; const Tweener = imports.ui.tweener; Tweener.init(); String.prototype.format = Format.format; }