/* -*- mode: js2 - indent-tabs-mode: nil - js2-basic-offset: 4 -*- */
/*jshint multistr:true */
/*jshint esnext:true */
/*global imports: true */
/*global global: true */
/*global log: true */
/**
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
**/
'use strict';
const St = imports.gi.St;
const Gio = imports.gi.Gio;
const GObject = imports.gi.GObject;
const Main = imports.ui.main;
const Mainloop = imports.mainloop;
const PanelMenu = imports.ui.panelMenu;
const Shell = imports.gi.Shell;
const MessageTray = imports.ui.messageTray;
const Atk = imports.gi.Atk;
const Config = imports.misc.config;
const INHIBIT_APPS_KEY = 'inhibit-apps';
const SHOW_INDICATOR_KEY = 'show-indicator';
const SHOW_NOTIFICATIONS_KEY = 'show-notifications';
const USER_ENABLED_KEY = 'user-enabled';
const RESTORE_KEY = 'restore-state';
const FULLSCREEN_KEY = 'enable-fullscreen';
const Gettext = imports.gettext.domain('gnome-shell-extension-caffeine');
const _ = Gettext.gettext;
const Me = imports.misc.extensionUtils.getCurrentExtension();
const Convenience = Me.imports.convenience;
const DBusSessionManagerIface = '\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
\
';
const DBusSessionManagerProxy = Gio.DBusProxy.makeProxyWrapper(DBusSessionManagerIface);
const DBusSessionManagerInhibitorIface = '\
\
\
\
\
\
';
const DBusSessionManagerInhibitorProxy = Gio.DBusProxy.makeProxyWrapper(DBusSessionManagerInhibitorIface);
const IndicatorName = "Caffeine";
const DisabledIcon = 'my-caffeine-off-symbolic';
const EnabledIcon = 'my-caffeine-on-symbolic';
let CaffeineIndicator;
let ShellVersion = parseInt(Config.PACKAGE_VERSION.split(".")[1]);
const Caffeine = GObject.registerClass(
class Caffeine extends PanelMenu.Button {
_init() {
super._init(null, IndicatorName);
this.actor.accessible_role = Atk.Role.TOGGLE_BUTTON;
this._settings = Convenience.getSettings();
this._settings.connect(`changed::${SHOW_INDICATOR_KEY}`, () => {
if (this._settings.get_boolean(SHOW_INDICATOR_KEY))
this.actor.show();
else
this.actor.hide();
});
if (!this._settings.get_boolean(SHOW_INDICATOR_KEY))
this.actor.hide();
this._sessionManager = new DBusSessionManagerProxy(Gio.DBus.session,
'org.gnome.SessionManager',
'/org/gnome/SessionManager');
this._inhibitorAddedId = this._sessionManager.connectSignal('InhibitorAdded', this._inhibitorAdded.bind(this));
this._inhibitorRemovedId = this._sessionManager.connectSignal('InhibitorRemoved', this._inhibitorRemoved.bind(this));
// From auto-move-windows@gnome-shell-extensions.gcampax.github.com
this._windowTracker = Shell.WindowTracker.get_default();
// ("screen" in global) is false on 3.28, although global.screen exists
if (typeof global.screen !== "undefined") {
this._screen = global.screen;
this._display = this._screen.get_display();
}
else {
this._screen = global.display;
this._display = this._screen;
}
// Connect after so the handler from ShellWindowTracker has already run
this._windowCreatedId = this._display.connect_after('window-created', this._mayInhibit.bind(this));
let shellwm = global.window_manager;
this._windowDestroyedId = shellwm.connect('destroy', this._mayUninhibit.bind(this));
this._icon = new St.Icon({
style_class: 'system-status-icon'
});
this._icon.gicon = Gio.icon_new_for_string(`${Me.path}/icons/${DisabledIcon}.svg`);
this._state = false;
// who has requested the inhibition
this._last_app = "";
this._last_cookie = "";
this._apps = [];
this._cookies = [];
this._objects = [];
this.actor.add_actor(this._icon);
this.actor.add_style_class_name('panel-status-button');
this.actor.connect('button-press-event', this.toggleState.bind(this));
// Restore user state
if (this._settings.get_boolean(USER_ENABLED_KEY) && this._settings.get_boolean(RESTORE_KEY)) {
this.toggleState();
}
// Enable caffeine when fullscreen app is running
if (this._settings.get_boolean(FULLSCREEN_KEY)) {
this._inFullscreenId = this._screen.connect('in-fullscreen-changed', this.toggleFullscreen.bind(this));
this.toggleFullscreen();
}
// List current windows to check if we need to inhibit
global.get_window_actors().map(window => {
this._mayInhibit(null, window.meta_window, null);
});
}
get inFullscreen() {
let nb_monitors = this._screen.get_n_monitors();
let inFullscreen = false;
for (let i=0; i {
if (this.inFullscreen && !this._apps.includes('fullscreen')) {
this.addInhibit('fullscreen');
}
});
if (!this.inFullscreen && this._apps.includes('fullscreen')) {
this.removeInhibit('fullscreen');
}
}
toggleState() {
if (this._state) {
this._apps.forEach(app_id => this.removeInhibit(app_id));
}
else {
this.addInhibit('user');
}
}
addInhibit(app_id) {
this._sessionManager.InhibitRemote(app_id,
0, "Inhibit by %s".format(IndicatorName), 12,
cookie => {
this._last_cookie = cookie;
this._last_app = app_id;
}
);
}
removeInhibit(app_id) {
let index = this._apps.indexOf(app_id);
this._sessionManager.UninhibitRemote(this._cookies[index]);
}
_inhibitorAdded(proxy, sender, [object]) {
this._sessionManager.GetInhibitorsRemote(([inhibitors]) => {
for(var i in inhibitors) {
let inhibitor = new DBusSessionManagerInhibitorProxy(Gio.DBus.session,
'org.gnome.SessionManager',
inhibitors[i]);
inhibitor.GetAppIdRemote(app_id => {
if (app_id != '' && app_id == this._last_app) {
if (this._last_app == 'user')
this._settings.set_boolean(USER_ENABLED_KEY, true);
this._apps.push(this._last_app);
this._cookies.push(this._last_cookie);
this._objects.push(object);
this._last_app = "";
this._last_cookie = "";
if (this._state === false) {
this._state = true;
this._icon.gicon = Gio.icon_new_for_string(`${Me.path}/icons/${EnabledIcon}.svg`);
if (this._settings.get_boolean(SHOW_NOTIFICATIONS_KEY) && !this.inFullscreen)
Main.notify(_('Auto suspend and screensaver disabled'));
}
}
});
}
});
}
_inhibitorRemoved(proxy, sender, [object]) {
let index = this._objects.indexOf(object);
if (index != -1) {
if (this._apps[index] == 'user')
this._settings.set_boolean(USER_ENABLED_KEY, false);
// Remove app from list
this._apps.splice(index, 1);
this._cookies.splice(index, 1);
this._objects.splice(index, 1);
if (this._apps.length === 0) {
this._state = false;
this._icon.gicon = Gio.icon_new_for_string(`${Me.path}/icons/${DisabledIcon}.svg`);
if(this._settings.get_boolean(SHOW_NOTIFICATIONS_KEY))
Main.notify(_('Auto suspend and screensaver enabled'));
}
}
}
_mayInhibit(display, window, noRecurse) {
let app = this._windowTracker.get_window_app(window);
if (!app) {
if (!noRecurse) {
// window is not tracked yet
Mainloop.idle_add(() => {
this._mayInhibit(display, window, true);
return false;
});
}
return;
}
let app_id = app.get_id();
let apps = this._settings.get_strv(INHIBIT_APPS_KEY);
if (apps.includes(app_id))
this.addInhibit(app_id);
}
_mayUninhibit(shellwm, actor) {
let window = actor.meta_window;
let app = this._windowTracker.get_window_app(window);
if (app) {
let app_id = app.get_id();
if (this._apps.includes(app_id))
this.removeInhibit(app_id);
}
}
destroy() {
// remove all inhibitors
this._apps.forEach(app_id => this.removeInhibit(app_id));
// disconnect from signals
if (this._settings.get_boolean(FULLSCREEN_KEY))
this._screen.disconnect(this._inFullscreenId);
if (this._inhibitorAddedId) {
this._sessionManager.disconnectSignal(this._inhibitorAddedId);
this._inhibitorAddedId = 0;
}
if (this._inhibitorRemovedId) {
this._sessionManager.disconnectSignal(this._inhibitorRemovedId);
this._inhibitorRemovedId = 0;
}
if (this._windowCreatedId) {
this._display.disconnect(this._windowCreatedId);
this._windowCreatedId = 0;
}
if (this._windowDestroyedId) {
global.window_manager.disconnect(this._windowDestroyedId);
this._windowDestroyedId = 0;
}
super.destroy();
}
});
function init(extensionMeta) {
Convenience.initTranslations();
}
function enable() {
CaffeineIndicator = new Caffeine();
Main.panel.addToStatusArea(IndicatorName, CaffeineIndicator);
}
function disable() {
CaffeineIndicator.destroy();
CaffeineIndicator = null;
}