hack to get indicator-multiload to show in cinnamon tray for 17.3 (doesn't work for Cinnamon 3 on Ubuntu 16.04 LTS)

Please post suggestions for improvement of Cinnamon on:
https://github.com/linuxmint/Cinnamon
Forum rules
Before you post read how to get help. Topics in this forum are automatically closed 6 months after creation.
Locked
psionprime

hack to get indicator-multiload to show in cinnamon tray for 17.3 (doesn't work for Cinnamon 3 on Ubuntu 16.04 LTS)

Post by psionprime »

I've never messed with jscript or used Mint for that matter but it irked me enough after installing the cinnamon version of 17.3 I could not see the stats so I dug into it.

I found a note on where someone added indicator support to Gnome Shell which I think this cinnamon thing is derived from (again, total newb to mint and cinnamon). Btw the guy is looking for a new maintainer for it. I read up on the clutter and cinnamon api's then butchered my way through /usr/share/cinnamon/applets/systray@cinnamon.org/applet.js to get the three panels I like, to be visible.

I'm completely unfamiliar with how you guys do things here so I'm not even going to try a patch as I am happy for now, sharing, and want to go to bed. Here is the raw code. I put a couple of comments in where I did a couple edits. // 2016-05-03 - tk

Code: Select all

const Lang = imports.lang;
const St = imports.gi.St;
const Clutter = imports.gi.Clutter;

const Applet = imports.ui.applet;
const PopupMenu = imports.ui.popupMenu;
const Main = imports.ui.main;
const Mainloop = imports.mainloop;
const SignalManager = imports.misc.signalManager;

const ICON_SCALE_FACTOR = .8; // for custom panel heights, 20 (default icon size) / 25 (default panel height)

// Override the factory and create an AppletPopupMenu instead of a PopupMenu
function IndicatorMenuFactory() {
   this._init.apply(this, arguments);
}

IndicatorMenuFactory.prototype = {
    __proto__: PopupMenu.PopupMenuFactory.prototype,

    _init: function() {
        PopupMenu.PopupMenuFactory.prototype._init.call(this);
    },

    _createShellItem: function(factoryItem, launcher, orientation) {
        // Decide whether it's a submenu or not
        let shellItem = null;
        let item_type = factoryItem.getFactoryType();
        if (item_type == PopupMenu.FactoryClassTypes.RootMenuClass)
            shellItem = new Applet.AppletPopupMenu(launcher, orientation);
        if (item_type == PopupMenu.FactoryClassTypes.SubMenuMenuItemClass)
            shellItem = new PopupMenu.PopupSubMenuMenuItem("FIXME");
        else if (item_type == PopupMenu.FactoryClassTypes.MenuSectionMenuItemClass)
            shellItem = new PopupMenu.PopupMenuSection();
        else if (item_type == PopupMenu.FactoryClassTypes.SeparatorMenuItemClass)
            shellItem = new PopupMenu.PopupSeparatorMenuItem('');
        else if (item_type == PopupMenu.FactoryClassTypes.MenuItemClass)
            shellItem = new PopupMenu.PopupIndicatorMenuItem("FIXME");
        return shellItem;
    }
};

function MyApplet(orientation, panel_height, instance_id) {
    this._init(orientation, panel_height, instance_id);
}

MyApplet.prototype = {
    __proto__: Applet.Applet.prototype,

    _init: function(orientation, panel_height, instance_id) {
        Applet.Applet.prototype._init.call(this, orientation, panel_height, instance_id);

        this.actor.remove_style_class_name("applet-box");
        this.actor.style="spacing: 5px;";

        this._signalManager = new SignalManager.SignalManager(this);

        // 2016-05-03 - tk - homogeneous: false as multiload has a hard coded width for my needs; when true the icons will spread out
        let manager = new Clutter.BoxLayout( { spacing: 2 * global.ui_scale,
                                               homogeneous: false,
                                               orientation: Clutter.Orientation.HORIZONTAL });

        this.manager_container = new Clutter.Actor( { layout_manager: manager } );

        this.actor.add_actor (this.manager_container);

        this.manager_container.show();

        this._statusItems = [];
        this._shellIndicators = {};
        this.menuFactory = new IndicatorMenuFactory();
        this.menuManager = new PopupMenu.PopupMenuManager(this);
        this.signalAdded = 0;
        this.signalRemoved = 0;
    },

    _addIndicatorSupport: function() {
        let currentIndicators = Main.indicatorManager.getIndicatorIds();
        for (let pos in currentIndicators) {
            let appIndicator = Main.indicatorManager.getIndicatorById(currentIndicators[pos]);
            this._onIndicatorAdded(Main.indicatorManager, appIndicator);
        }
        if (this.signalAdded == 0)
            this.signalAdded = Main.indicatorManager.connect('indicator-added', Lang.bind(this, this._onIndicatorAdded));
        if (this.signalRemoved == 0)
            this.signalRemoved = Main.indicatorManager.connect('indicator-removed', Lang.bind(this, this._onIndicatorRemoved));
    },

    _removeIndicatorSupport: function() {
        if (this.signalAdded) {
            Main.indicatorManager.disconnect(this.signalAdded);
            this.signalAdded = 0;
        }
        if (this.signalRemoved) {
            Main.indicatorManager.disconnect(this.signalRemoved);
            this.signalRemoved = 0;
        }
        this._shellIndicators.forEach(function(iconActor) {
            iconActor.destroy();
        });
        this._shellIndicators = {};
    },

    _onIndicatorAdded: function(manager, appIndicator) {
        if (!(appIndicator.id in this._shellIndicators)) {
            let hiddenIcons = Main.systrayManager.getRoles();

            if (hiddenIcons.indexOf(appIndicator.id) != -1 ) {
                // We've got an applet for that
                global.log("Hiding indicator (role already handled): " + appIndicator.id);
                return;
            }
            else if (["quassel"].indexOf(appIndicator.id) != -1) {
                // Blacklist some of the icons
                // quassel: The proper icon in Quassel is "QuasselIRC", this is a fallback icon which Quassel launches when it fails to detect
                // our indicator support (i.e. when Cinnamon is restarted for instance)
                // The problem is.. Quassel doesn't kill that icon when it creates QuasselIRC again..
                global.log("Hiding indicator (blacklisted): " + appIndicator.id);
                return;
            }
            else {
                global.log("Adding indicator: " + appIndicator.id);
            }

            let iconActor = appIndicator.getIconActor(this._getIndicatorSize(appIndicator));
            iconActor._applet = this;

            this._shellIndicators[appIndicator.id] = iconActor;

            this.actor.add_actor(iconActor.actor);
            appIndicator.createMenuClientAsync(Lang.bind(this, function(client) {
                if (client != null) {
                    let newMenu = client.getShellMenu();
                    if (!newMenu) {
                        newMenu = this.menuFactory.buildShellMenu(client, iconActor, this._applet_context_menu._arrowSide);
                        this.menuManager.addMenu(newMenu);
                    }
                    iconActor.setMenu(newMenu);
                }
            }));
        }
    },

    _getIndicatorSize: function(appIndicator) {
        if (this._scaleMode)
            return this._panelHeight * ICON_SCALE_FACTOR;
        return 16;
    },

    _onIndicatorRemoved: function(manager, appIndicator) {
        if (appIndicator.id in this._shellIndicators) {
            global.log("Removing indicator: " + appIndicator.id);
            let iconActor = this._shellIndicators[appIndicator.id];
            delete this._shellIndicators[appIndicator.id];
            iconActor.destroy();
        }
    },

    on_applet_clicked: function(event) {
    },

    on_applet_removed_from_panel: function () {
        this._signalManager.disconnectAllSignals();
        this._removeIndicatorSupport();
    },

    on_applet_added_to_panel: function() {
        Main.statusIconDispatcher.start(this.actor.get_parent().get_parent());

        this._signalManager.connect(Main.statusIconDispatcher, 'status-icon-added', this._onTrayIconAdded);
        this._signalManager.connect(Main.statusIconDispatcher, 'status-icon-removed', this._onTrayIconRemoved);
        this._signalManager.connect(Main.statusIconDispatcher, 'before-redisplay', this._onBeforeRedisplay);
        this._signalManager.connect(Main.systrayManager, "changed", Main.statusIconDispatcher.redisplay, Main.statusIconDispatcher);
        this._addIndicatorSupport();
    },

    on_panel_height_changed: function() {
        Main.statusIconDispatcher.redisplay();
        for (let id in this._shellIndicators) {
            let indicator = Main.indicatorManager.getIndicatorById(id);
            if (indicator) {
                let size = this._getIndicatorSize(indicator);
                this._shellIndicators[id].setSize(size);
            }
        }
    },

    _onBeforeRedisplay: function() {
        // Mark all icons as obsolete
        // There might still be pending delayed operations to insert/resize of them
        // And that would crash Cinnamon
        for (var i = 0; i < this._statusItems.length; i++) {
            this._statusItems[i].obsolete = true;
        }
        this._statusItems = [];

        let children = this.manager_container.get_children();
        for (var i = 0; i < children.length; i++) {
            children[i].destroy();
        }
    },

    _onTrayIconAdded: function(o, icon, role) {
        try {
            let hiddenIcons = Main.systrayManager.getRoles();

            if (hiddenIcons.indexOf(role) != -1 ) {
                // We've got an applet for that
                global.log("Hiding systray: " + role);
                return;
            }

            global.log("Adding systray: " + role + " (" + icon.get_width() + "x" + icon.get_height() + "px)");

            if (icon.get_parent())
                icon.get_parent().remove_child(icon);

            icon.obsolete = false;
            this._statusItems.push(icon);

            if (["pidgin"].indexOf(role) != -1) {
                // Delay pidgin insertion by 10 seconds
                // Pidgin is very weird.. it starts with a small icon
                // Then replaces that icon with a bigger one when the connection is established
                // Pidgin can be fixed by inserting or resizing after a delay
                // The delay is big because resizing/inserting too early
                // makes pidgin invisible (in absence of disk cache).. even if we resize/insert again later
                this._insertStatusItemLater(role, icon, -1, 10000);
            }
            else if (["shutter", "filezilla", "dropbox", "thunderbird", "unknown", "blueberry-tray.py", "mintupdate.py"].indexOf(role) != -1) {
                // Delay insertion by 1 second
                // This fixes an invisible icon in the absence of disk cache for : shutter
                // filezilla, dropbox, thunderbird, blueberry, mintupdate are known to show up in the wrong size or position, this chould fix them as well
                // Note: as of Oct 2015, the dropbox systray is calling itself "unknown"
                this._insertStatusItemLater(role, icon, -1, 1000);
            }
           // 2016-05-03 - tk - added multiload delay to hopefully have it on the end
            else if (["multiload"].indexOf(role) != -1) {
                // Delay insertion by 2 seconds
                this._insertStatusItemLater(role, icon, -1, 2000);
            }
            else {
                // Delay all other apps by 1 second...
                // For many of them, we don't need to do that,
                // It's a small delay though and that fixes most buggy apps
                // And we're far from having an exhaustive list of them..
                this._insertStatusItemLater(role, icon, -1, 1000);
            }

        } catch (e) {
            global.logError(e);
        }
    },

    _insertStatusItemLater: function(role, icon, position, delay) {
        // Inserts an icon in the systray after a delay (useful for buggy icons)
        // Delaying the insertion of pidgin by 10 seconds for instance is known to fix it on empty disk cache
        let timerId = Mainloop.timeout_add(delay, Lang.bind(this, function() {
            this._insertStatusItem(role, icon, position);
            Mainloop.source_remove(timerId);
        }));
    },

    _onTrayIconRemoved: function(o, icon) {
        icon.obsolete = true;
        for (var i = 0; i < this._statusItems.length; i++) {
            if (this._statusItems[i] == icon) {
                this._statusItems.splice(i, 1);
            }
        }
        this.manager_container.remove_child(icon);
        icon.destroy();
    },

    _insertStatusItem: function(role, icon, position) {
        if (icon.obsolete == true) {
            return;
        }
        let children = this.manager_container.get_children();
        let i;
        for (i = children.length - 1; i >= 0; i--) {
            let rolePosition = children[i]._rolePosition;
            if (position > rolePosition) {
                this.manager_container.insert_child_at_index(icon, i + 1);
                break;
            }
        }
        if (i == -1) {
            // If we didn't find a position, we must be first
            this.manager_container.insert_child_at_index(icon, 0);
        }
        icon._rolePosition = position;
        if (this._scaleMode) {
            let timerId = Mainloop.timeout_add(500, Lang.bind(this, function() {
                this._resizeStatusItem(role, icon);
                Mainloop.source_remove(timerId);
            }));
        }
    },

    _resizeStatusItem: function(role, icon) {
        if (icon.obsolete == true) {
            return;
        }
        let size;
        let disp_size = this._panelHeight * ICON_SCALE_FACTOR;
        if (["shutter", "filezilla"].indexOf(role) != -1) {
            global.log("Not resizing " + role + " as it's known to be buggy (" + icon.get_width() + "x" + icon.get_height() + "px)");
        }
        else {
            if (disp_size < 22) {
                size = 16;
            }
            else if (disp_size < 32) {
                size = 22;
            }
            else if (disp_size < 48) {
                size = 32;
            }
            else {
                size = 48;
            }
           // 2016-05-03 - tk - added multiload hard coded width as I usually have 3 monitors up; change it for your needs
           if (["multiload"].indexOf(role) != -1) {
                icon.set_x_expand(true)
                icon.set_size(126, size);
                //global.log("Not resizing " + role + "'s width as it's known to be buggy (forcing 120w" + "x" + icon.get_height() + "h px)");
            }
            else {
                icon.set_size(size, size);
            }
            global.log("Resized " + role + " with normalized size (" + icon.get_width() + "x" + icon.get_height() + "px)");
            //Note: dropbox doesn't scale, even though we resize it...
        }
    },


};

function main(metadata, orientation, panel_height, instance_id) {
    let myApplet = new MyApplet(orientation, panel_height, instance_id);
    return myApplet;
}

As a suggestion for someone who knows what they are doing (not this guy), I'd see about letting the clutter layout do it's thing and allow it to auto expand/arrange.


Image


Good Luck !
Last edited by LockBot on Wed Dec 28, 2022 7:16 am, edited 2 times in total.
Reason: Topic automatically closed 6 months after creation. New replies are no longer allowed.
psionprime

Re: hack to get indicator-multiload to show in cinnamon tray for 17.3

Post by psionprime »

I put Cinnamon 3 on Ubuntu Studio 16.04 LTS. It looks good aside that the hack to get indicator-multiload to show doesn't work. The original applet.js appears the same as the previous version but no joy after applying the hack. Looking into it.
psionprime

Re: hack to get indicator-multiload to show in cinnamon tray for 17.3 (doesn't work for Cinnamon 3 on Ubuntu 16.04 LTS)

Post by psionprime »

Okay, what I've found so far by adding several global.log() debug messages. It seems tray-icon-added is not being called so the call chain for resizing is never hit. Below is the log when my debug version of the systray applet is started. This was being called prior to Cinnamon 3 but....may be a mint thing, though I didn't see changes in the cinnamon git repository that should affect it. I'll probably wind up emailing a dev and see about hacking a call to the resize stuff when it calls panel resize.

The log with normal sized panel then I resized it. Just indicator-multiload and software update (which sucks in Ubuntu btw as it rarely works right):

Code: Select all

info t=2016-05-16T04:54:57.861Z Entered: IndicatorMenuFactory
info t=2016-05-16T04:54:57.861Z Entered: MyApplet
info t=2016-05-16T04:54:57.864Z Entered: on_applet_added_to_panel
info t=2016-05-16T04:54:57.869Z Entered: _addIndicatorSupport
info t=2016-05-16T04:54:57.869Z Entered: _onIndicatorAdded
info t=2016-05-16T04:54:57.870Z Adding indicator: multiload
info t=2016-05-16T04:54:57.870Z Entered: _getIndicatorSize
info t=2016-05-16T04:54:57.890Z Entered: _onIndicatorAdded
info t=2016-05-16T04:54:57.890Z Hiding indicator (role already handled): nm-applet
info t=2016-05-16T04:54:57.890Z Entered: _onIndicatorAdded
info t=2016-05-16T04:54:57.890Z Adding indicator: software-update-available
info t=2016-05-16T04:54:57.891Z Entered: _getIndicatorSize
info t=2016-05-16T04:54:57.892Z Loaded applet systray-tki@cinnamon.org in 36 ms
info t=2016-05-16T04:54:57.892Z Entered: _onBeforeRedisplay
info t=2016-05-16T04:55:47.854Z Entered: on_panel_height_changed
info t=2016-05-16T04:55:47.854Z Entered: _onBeforeRedisplay
info t=2016-05-16T04:55:47.854Z Entered: _getIndicatorSize
info t=2016-05-16T04:55:47.872Z Entered: _getIndicatorSize
I've added a debug message inside statusIconDispatcher.js and cinnamon-tray-manager.c appears to not be firing a added message so hmmmm.
JosephM
Level 6
Level 6
Posts: 1459
Joined: Sun May 26, 2013 6:25 pm

Re: hack to get indicator-multiload to show in cinnamon tray for 17.3 (doesn't work for Cinnamon 3 on Ubuntu 16.04 LTS)

Post by JosephM »

You probably know this but in Cinnamon3 indicator support is disabled by default for the systray. You can re-enable it in Cinnamon-settings->General.
When I give opinions, they are my own. Not necessarily those of any other Linux Mint developer or the Linux Mint project as a whole.
psionprime

Re: hack to get indicator-multiload to show in cinnamon tray for 17.3 (doesn't work for Cinnamon 3 on Ubuntu 16.04 LTS)

Post by psionprime »

Thanks for the memory jog. I didn't remember it (new to cinnamon) but must have set it as that and logging are on.

The tray add emit is not firing that I can tell for any indicator. I have worked out there is a clutter box for the tray and a box for every item. I have the item box for multiload forced to a width I want but the icon is still tiny. Drilling down to it and learning more than I ever wanted about java-script, clutter, gnome-shell and gjs. Everyone should do it for the rich appreciation it brings for the cinnamon development team :)
Locked

Return to “Cinnamon”