diff --git a/INSTALL.md b/INSTALL.md index f0c4e47..fb97281 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -59,6 +59,29 @@ _Make a ZIP package for GNOME Shell:_ ./autogen.sh && make && make zip-file ``` +### Local development (GNOME Shell 45+) +_Run the extension directly from a local checkout:_ +```sh +mkdir -p ~/.local/share/gnome-shell/extensions +ln -snf "$(pwd)" ~/.local/share/gnome-shell/extensions/cpufreq@konkor +``` +Then enable it: +```sh +gnome-extensions enable cpufreq@konkor +``` +If GNOME Shell doesn't pick up changes, log out/in (Wayland) or restart GNOME Shell. + +_Alternatively, install a fresh bundle:_ +```sh +gnome-extensions pack . --force +# installs into ~/.local/share/gnome-shell/extensions +gnome-extensions install --force ./cpufreq@konkor.shell-extension.zip +``` +If settings are missing, rebuild schemas: +```sh +glib-compile-schemas schemas/ +``` + ## Complete uninstall and removing of stored settings. It can be useful if you have saved broken settings values or to clean up previous installation. diff --git a/Makefile.am b/Makefile.am index ef87a99..d567252 100644 --- a/Makefile.am +++ b/Makefile.am @@ -16,6 +16,7 @@ js_DATA = \ convenience.js \ extension.js \ prefs.js \ + prefs_legacy.js \ metadata.json \ stylesheet.css \ konkor.cpufreq.policy \ diff --git a/common/Preferences.js b/common/Preferences.js index ac9899a..7330078 100644 --- a/common/Preferences.js +++ b/common/Preferences.js @@ -17,7 +17,7 @@ const Lang = imports.lang; const APPDIR = getCurrentFile ()[1]; -const Prefs = imports.prefs; +const Prefs = imports.prefs_legacy; var Preferences = new Lang.Class ({ Name: 'Preferences', diff --git a/extension.js b/extension.js index 7522e2c..82f758f 100644 --- a/extension.js +++ b/extension.js @@ -8,34 +8,27 @@ * with this program. If not, see . */ -const Lang = imports.lang; -const GObject = imports.gi.GObject; -const GLib = imports.gi.GLib; -const Gio = imports.gi.Gio; -const St = imports.gi.St; -const Main = imports.ui.main; -const PanelMenu = imports.ui.panelMenu; - -const ExtensionUtils = imports.misc.extensionUtils; - -const Me = ExtensionUtils.getCurrentExtension (); -const Logger = Me.imports.common.Logger; -const Convenience = Me.imports.convenience; -const EXTENSIONDIR = Me.dir.get_path (); -const APP_PATH = EXTENSIONDIR + "/cpufreq-application"; +import GObject from 'gi://GObject'; +import GLib from 'gi://GLib'; +import Gio from 'gi://Gio'; +import St from 'gi://St'; + +import * as Main from 'resource:///org/gnome/shell/ui/main.js'; +import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js'; +import {Extension} from 'resource:///org/gnome/shell/extensions/extension.js'; const SAVE_SETTINGS_KEY = 'save-settings'; -const EXTENSION_MODE_KEY= 'extension-mode'; -const SHOW_SPLASH_KEY = 'show-splash'; -const PROFILE_ID_KEY = 'profile-id'; -const MONITOR_KEY = 'monitor'; -const EPROFILES_KEY = 'event-profiles'; -const LABEL_KEY = 'label' -const LABEL_SHOW_KEY = 'label-show'; -const UNITS_SHOW_KEY = 'units-show'; -const FREQ_SHOW_KEY = 'frequency-show'; -const GOVS_SHOW_KEY = 'governors-show'; -const LOAD_SHOW_KEY = 'load-show'; +const EXTENSION_MODE_KEY = 'extension-mode'; +const SHOW_SPLASH_KEY = 'show-splash'; +const PROFILE_ID_KEY = 'profile-id'; +const MONITOR_KEY = 'monitor'; +const EPROFILES_KEY = 'event-profiles'; +const LABEL_KEY = 'label'; +const LABEL_SHOW_KEY = 'label-show'; +const UNITS_SHOW_KEY = 'units-show'; +const FREQ_SHOW_KEY = 'frequency-show'; +const GOVS_SHOW_KEY = 'governors-show'; +const LOAD_SHOW_KEY = 'load-show'; const COLOR_SHOW_KEY = 'color-show'; const COLOR_SHOW_CUSTOM_KEY = 'color-show-custom'; @@ -43,499 +36,593 @@ const COLOR_SHOW_CUSTOM_NORMAL_KEY = 'color-show-custom-normal'; const COLOR_SHOW_CUSTOM_WARNING_KEY = 'color-show-custom-warning'; const COLOR_SHOW_CUSTOM_CRITICAL_KEY = 'color-show-custom-critical'; -let color_show = false; -let color_show_custom = false; -let color_show_default_normal = ''; //'#33d552'; -let color_show_default_warning = 'orange'; -let color_show_default_critical = 'red'; -let color_show_custom_normal = '#ebebeb'; -let color_show_custom_warning = '#ebebeb'; -let color_show_custom_critical = '#ff0000'; - -//const SETTINGS_ID = 'org.gnome.shell.extensions.cpufreq'; - -let event = 0; -let event_style = 0; -let monitor_event = 0; -let settingsID, powerID, scheduleID; - -let save = false; -let extmode = true; -let splash = true; -let label_text = ""; -let label_show = false; -let units_show = true; -let frequency_show = true; -let governor_show = false; -let load_show = false; -let title_text = "\u26A0"; -let title_style = ""; -let monitor_timeout = 500; -let eprofiles = [ - {percent:0, event:0, guid:""}, - {percent:100, event:1, guid:""} -]; -let first_boot = true; -let guid_battery = ""; - const UP_BUS_NAME = 'org.freedesktop.UPower'; const UP_OBJECT_PATH = '/org/freedesktop/UPower/devices/DisplayDevice'; -const DisplayDeviceInterface = ' \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -'; +const DisplayDeviceInterface = ` + + + + + + + + + +`; const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(DisplayDeviceInterface); const BUS_NAME = 'org.konkor.cpufreq.service'; const OBJECT_PATH = '/org/konkor/cpufreq/service'; -const CpufreqServiceIface = ' \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ - \ -'; -const CpufreqServiceProxy = Gio.DBusProxy.makeProxyWrapper (CpufreqServiceIface); - -const CpuFreq = { - init: function () { - this.settings = Convenience.getSettings(); - this.on_settings (null, null); - - this.statusLabel = new St.Label ({ - text: title_text, y_expand: true, y_align: 2, style_class:'cpufreq-text' +const CpufreqServiceIface = ` + + + + + + + + + + + + +`; +const CpufreqServiceProxy = Gio.DBusProxy.makeProxyWrapper(CpufreqServiceIface); + +function byteArrayToString(value) { + if (typeof value === 'string') + return value; + + if (value instanceof Uint8Array) + return new TextDecoder().decode(value); + + return String(value); +} + +function getUiGroup() { + return Main.uiGroup ?? Main.layoutManager?.uiGroup; +} + +function addChild(container, child) { + if (!container) + return; + + if (container.add_child) + container.add_child(child); + else if (container.add_actor) + container.add_actor(child); +} + +function removeChild(container, child) { + if (!container) + return; + + if (container.remove_child) + container.remove_child(child); + else if (container.remove_actor) + container.remove_actor(child); +} + +function removeActor(actor) { + if (!actor) + return false; + + let parent = actor.get_parent?.(); + removeChild(parent ?? getUiGroup(), actor); + actor.destroy(); + return false; +} + +function sessionBusNameHasOwner(name) { + try { + const result = Gio.DBus.session.call_sync( + 'org.freedesktop.DBus', + '/org/freedesktop/DBus', + 'org.freedesktop.DBus', + 'NameHasOwner', + new GLib.Variant('(s)', [name]), + null, + Gio.DBusCallFlags.NONE, + -1, + null + ); + + return result?.deepUnpack?.()?.[0] ?? false; + } catch { + return false; + } +} + +const FrequencyIndicator = GObject.registerClass( +class FrequencyIndicator extends PanelMenu.Button { + _init(extension) { + super._init(0.0, 'CPU Frequency Indicator', false); + + this._extension = extension; + this._extensionDir = extension.path; + this._appPath = `${this._extensionDir}/cpufreq-application`; + + this._event = 0; + this._eventStyle = 0; + this._monitorEventId = 0; + this._settingsChangedId = 0; + this._powerChangedId = 0; + this._scheduleId = 0; + + this._titleText = '⚠'; + this._titleStyle = ''; + + this._colorShow = false; + this._colorShowCustom = false; + this._colorShowDefaultNormal = ''; + this._colorShowDefaultWarning = 'orange'; + this._colorShowDefaultCritical = 'red'; + this._colorShowCustomNormal = '#ebebeb'; + this._colorShowCustomWarning = '#ebebeb'; + this._colorShowCustomCritical = '#ff0000'; + + this._save = false; + this._extensionMode = true; + this._splashEnabled = true; + this._labelText = ''; + this._labelShow = false; + this._unitsShow = true; + this._frequencyShow = true; + this._governorShow = false; + this._loadShow = false; + + this._monitorTimeout = 500; + this._eprofiles = [ + {percent: 0, event: 0, guid: ''}, + {percent: 100, event: 1, guid: ''}, + ]; + + this._firstBoot = true; + this._guidBattery = ''; + this._guid = ''; + + this._settings = this._extension.getSettings(); + this._onSettingsChanged(null, null); + + this._statusLabel = new St.Label({ + text: this._titleText, + y_expand: true, + y_align: 2, + style_class: 'cpufreq-text', }); - this.statusLabel.style = title_style; - let _box = new St.BoxLayout(); - _box.add_actor (this.statusLabel); - this.add_actor (_box); - this.connect ('button-press-event', () => { - var args = extmode ? "--extension" : ""; - if (splash) - if (!this.app_running) this.show_splash (); - if (!guid_battery || (guid_battery == this.guid)) this.launch_app (args); - else this.launch_app (args + " --no-save"); + this._statusLabel.style = this._titleStyle; + + let box = new St.BoxLayout(); + addChild(box, this._statusLabel); + addChild(this, box); + + this.connect('button-press-event', () => { + let args = this._extensionMode ? '--extension' : ''; + if (this._splashEnabled && !this.app_running) + this.show_splash(); + + if (!this._guidBattery || this._guidBattery === this._guid) + this.launch_app(args); + else + this.launch_app(`${args} --no-save`); }); - if (!monitor_timeout) this.statusLabel.set_text (this.get_title ()); - this.add_event (); + if (!this._monitorTimeout) + this._statusLabel.set_text(this.get_title()); - //TODO: Workaround updating title - this.settings.set_boolean (SAVE_SETTINGS_KEY, !save); - this.settings.set_boolean (SAVE_SETTINGS_KEY, save); + this.add_event(); - if (settingsID) this.settings.disconnect (settingsID); - settingsID = this.settings.connect ("changed", this.on_settings.bind (this)); + // Workaround: force a settings change to update title. + this._settings.set_boolean(SAVE_SETTINGS_KEY, !this._save); + this._settings.set_boolean(SAVE_SETTINGS_KEY, this._save); - this.power = new PowerManagerProxy (Gio.DBus.system, UP_BUS_NAME, UP_OBJECT_PATH, (proxy, e) => { - if (e) { - error (e.message); - return; - } - this.on_power_state (proxy.State, proxy.Percentage); - if (save && first_boot && !guid_battery) this.launch_app ("-p user"); - first_boot = false; - GLib.timeout_add (0, 8000, () => { - powerID = this.power.connect ('g-properties-changed', (o,a) => { - //a = a{sv} - this.on_power_state (this.power.State, this.power.Percentage); + this._settingsChangedId = this._settings.connect( + 'changed', + this._onSettingsChanged.bind(this) + ); + + this._power = new PowerManagerProxy( + Gio.DBus.system, + UP_BUS_NAME, + UP_OBJECT_PATH, + (proxy, error) => { + if (error) { + logError(error, '[cpufreq] UPower proxy error'); + return; + } + + this.on_power_state(proxy.State, proxy.Percentage); + if (this._save && this._firstBoot && !this._guidBattery) + this.launch_app('-p user'); + this._firstBoot = false; + + GLib.timeout_add(GLib.PRIORITY_DEFAULT, 8000, () => { + this._powerChangedId = this._power.connect( + 'g-properties-changed', + () => this.on_power_state(this._power.State, this._power.Percentage) + ); + return GLib.SOURCE_REMOVE; }); - }); - }); - }, + } + ); + } - on_settings: function (o, key) { + _onSettingsChanged(_settings, key) { let s; - o = o || this.settings; + let settings = _settings || this._settings; if (!key) { - this.guid = o.get_string (PROFILE_ID_KEY); - monitor_timeout = o.get_int (MONITOR_KEY); - save = o.get_boolean (SAVE_SETTINGS_KEY); - extmode = o.get_boolean (EXTENSION_MODE_KEY); - splash = o.get_boolean (SHOW_SPLASH_KEY); - label_text = o.get_string (LABEL_KEY); - label_show = o.get_boolean (LABEL_SHOW_KEY); - units_show = o.get_boolean (UNITS_SHOW_KEY); - frequency_show = o.get_boolean (FREQ_SHOW_KEY); - governor_show = o.get_boolean (GOVS_SHOW_KEY); - load_show = o.get_boolean (LOAD_SHOW_KEY); - color_show = o.get_boolean (COLOR_SHOW_KEY); - color_show_custom = o.get_boolean (COLOR_SHOW_CUSTOM_KEY); - color_show_custom_normal = o.get_string (COLOR_SHOW_CUSTOM_NORMAL_KEY); - color_show_custom_warning = o.get_string (COLOR_SHOW_CUSTOM_WARNING_KEY); - color_show_custom_critical = o.get_string (COLOR_SHOW_CUSTOM_CRITICAL_KEY); - s = o.get_string (EPROFILES_KEY); - if (s) eprofiles = JSON.parse (s); + this._guid = settings.get_string(PROFILE_ID_KEY); + this._monitorTimeout = settings.get_int(MONITOR_KEY); + this._save = settings.get_boolean(SAVE_SETTINGS_KEY); + this._extensionMode = settings.get_boolean(EXTENSION_MODE_KEY); + this._splashEnabled = settings.get_boolean(SHOW_SPLASH_KEY); + this._labelText = settings.get_string(LABEL_KEY); + this._labelShow = settings.get_boolean(LABEL_SHOW_KEY); + this._unitsShow = settings.get_boolean(UNITS_SHOW_KEY); + this._frequencyShow = settings.get_boolean(FREQ_SHOW_KEY); + this._governorShow = settings.get_boolean(GOVS_SHOW_KEY); + this._loadShow = settings.get_boolean(LOAD_SHOW_KEY); + + this._colorShow = settings.get_boolean(COLOR_SHOW_KEY); + this._colorShowCustom = settings.get_boolean(COLOR_SHOW_CUSTOM_KEY); + this._colorShowCustomNormal = settings.get_string(COLOR_SHOW_CUSTOM_NORMAL_KEY); + this._colorShowCustomWarning = settings.get_string(COLOR_SHOW_CUSTOM_WARNING_KEY); + this._colorShowCustomCritical = settings.get_string(COLOR_SHOW_CUSTOM_CRITICAL_KEY); + + s = settings.get_string(EPROFILES_KEY); + if (s) + this._eprofiles = JSON.parse(s); } - if (key == MONITOR_KEY) { - monitor_timeout = o.get_int (MONITOR_KEY); - if (monitor_event) { - GLib.source_remove (monitor_event); - monitor_event = 0; + if (key === MONITOR_KEY) { + this._monitorTimeout = settings.get_int(MONITOR_KEY); + if (this._monitorEventId) { + GLib.source_remove(this._monitorEventId); + this._monitorEventId = 0; } - monitor_event = GLib.timeout_add (100, 1000, this.add_event.bind (this)); - } else if (key == PROFILE_ID_KEY) { - this.guid = o.get_string (PROFILE_ID_KEY); - } else if (key == EPROFILES_KEY) { - s = o.get_string (EPROFILES_KEY); - if (s) eprofiles = JSON.parse (s); - } else if (key == EXTENSION_MODE_KEY) { - extmode = o.get_boolean (EXTENSION_MODE_KEY); - } else if (key == SHOW_SPLASH_KEY) { - splash = o.get_boolean (SHOW_SPLASH_KEY); - } else if (key == LABEL_KEY) { - label_text = o.get_string (LABEL_KEY); - } else if (key == LABEL_SHOW_KEY) { - label_show = o.get_boolean (LABEL_SHOW_KEY); - } else if (key == UNITS_SHOW_KEY) { - units_show = o.get_boolean (UNITS_SHOW_KEY); - } else if (key == FREQ_SHOW_KEY) { - frequency_show = o.get_boolean (FREQ_SHOW_KEY); - } else if (key == GOVS_SHOW_KEY) { - governor_show = o.get_boolean (GOVS_SHOW_KEY); - } else if (key == LOAD_SHOW_KEY) { - load_show = o.get_boolean (LOAD_SHOW_KEY); - }else if (key == COLOR_SHOW_KEY) { - color_show = o.get_boolean (COLOR_SHOW_KEY); - } else if (key == COLOR_SHOW_CUSTOM_KEY) { - color_show_custom = o.get_boolean (COLOR_SHOW_CUSTOM_KEY); - } else if (key == COLOR_SHOW_CUSTOM_NORMAL_KEY) { - color_show_custom_normal = o.get_string (COLOR_SHOW_CUSTOM_NORMAL_KEY); - } else if (key == COLOR_SHOW_CUSTOM_WARNING_KEY) { - color_show_custom_warning = o.get_string (COLOR_SHOW_CUSTOM_WARNING_KEY); - } else if (key == COLOR_SHOW_CUSTOM_CRITICAL_KEY) { - color_show_custom_critical = o.get_string (COLOR_SHOW_CUSTOM_CRITICAL_KEY); + this._monitorEventId = GLib.timeout_add( + GLib.PRIORITY_DEFAULT, + 1000, + this.add_event.bind(this) + ); + } else if (key === PROFILE_ID_KEY) { + this._guid = settings.get_string(PROFILE_ID_KEY); + } else if (key === EPROFILES_KEY) { + s = settings.get_string(EPROFILES_KEY); + if (s) + this._eprofiles = JSON.parse(s); + } else if (key === EXTENSION_MODE_KEY) { + this._extensionMode = settings.get_boolean(EXTENSION_MODE_KEY); + } else if (key === SHOW_SPLASH_KEY) { + this._splashEnabled = settings.get_boolean(SHOW_SPLASH_KEY); + } else if (key === LABEL_KEY) { + this._labelText = settings.get_string(LABEL_KEY); + } else if (key === LABEL_SHOW_KEY) { + this._labelShow = settings.get_boolean(LABEL_SHOW_KEY); + } else if (key === UNITS_SHOW_KEY) { + this._unitsShow = settings.get_boolean(UNITS_SHOW_KEY); + } else if (key === FREQ_SHOW_KEY) { + this._frequencyShow = settings.get_boolean(FREQ_SHOW_KEY); + } else if (key === GOVS_SHOW_KEY) { + this._governorShow = settings.get_boolean(GOVS_SHOW_KEY); + } else if (key === LOAD_SHOW_KEY) { + this._loadShow = settings.get_boolean(LOAD_SHOW_KEY); + } else if (key === COLOR_SHOW_KEY) { + this._colorShow = settings.get_boolean(COLOR_SHOW_KEY); + } else if (key === COLOR_SHOW_CUSTOM_KEY) { + this._colorShowCustom = settings.get_boolean(COLOR_SHOW_CUSTOM_KEY); + } else if (key === COLOR_SHOW_CUSTOM_NORMAL_KEY) { + this._colorShowCustomNormal = settings.get_string(COLOR_SHOW_CUSTOM_NORMAL_KEY); + } else if (key === COLOR_SHOW_CUSTOM_WARNING_KEY) { + this._colorShowCustomWarning = settings.get_string(COLOR_SHOW_CUSTOM_WARNING_KEY); + } else if (key === COLOR_SHOW_CUSTOM_CRITICAL_KEY) { + this._colorShowCustomCritical = settings.get_string(COLOR_SHOW_CUSTOM_CRITICAL_KEY); } - //TODO: statusLabel - if ((key == LABEL_KEY) && !monitor_timeout) this.statusLabel.set_text (this.get_title ()); - - /*if ((key == "power-state") || (key == "power-percentage")) { - debug ("power-state changed..."); - this.on_power_state (o.get_uint ("power-state"), o.get_double ("power-percentage")); - }*/ - }, - - on_power_state: function (state, percentage) { - let id = eprofiles[1].guid; - //debug ("on_power_state: %s %s%%".format (this.power.State, this.power.Percentage)); - debug ("on_power_state: %s %s%%".format (state, percentage)); - if (!id) return; - if (state == 2) { - //on battery - if (id == guid_battery) return; - if (percentage < eprofiles[1].percent) { - this.schedule_profile ('--no-save -p ' + id); - guid_battery = id; + if (key === LABEL_KEY && !this._monitorTimeout) + this._statusLabel.set_text(this.get_title()); + } + + on_power_state(state, percentage) { + let id = this._eprofiles?.[1]?.guid; + if (!id) + return; + + if (state === 2) { + // On battery. + if (id === this._guidBattery) + return; + if (percentage < this._eprofiles[1].percent) { + this.schedule_profile(`--no-save -p ${id}`); + this._guidBattery = id; } } else { - //restoring prev state - if (guid_battery == this.guid) return; - this.schedule_profile ('-p user'); - guid_battery = this.guid; + // Restoring previous state. + if (this._guidBattery === this._guid) + return; + this.schedule_profile('-p user'); + this._guidBattery = this._guid; } - }, - - unschedule_profile: function () { - GLib.source_remove (scheduleID); - scheduleID = 0; - }, - - schedule_profile: function (options) { - if (scheduleID) this.unschedule_profile (); - scheduleID = GLib.timeout_add (0, 5000, () => { - this.launch_app (options); - scheduleID = 0; + } + + unschedule_profile() { + if (!this._scheduleId) + return; + GLib.source_remove(this._scheduleId); + this._scheduleId = 0; + } + + schedule_profile(options) { + if (this._scheduleId) + this.unschedule_profile(); + + this._scheduleId = GLib.timeout_add(GLib.PRIORITY_DEFAULT, 5000, () => { + this.launch_app(options); + this._scheduleId = 0; + return GLib.SOURCE_REMOVE; }); - }, - - launch_app: function (options) { - let extra = ""; - /*if (Logger.debug_lvl == 2) extra = " --debug"; - else if (Logger.debug_lvl == 1) extra = " --verbose";*/ - options = options || ""; - info ("launch_app " + options + extra); - GLib.spawn_command_line_async ("%s %s".format (APP_PATH, options + extra)); - }, - - get app_running () { - let res = GLib.spawn_command_line_sync ("ps -A"); - let o, n; - if (res[0]) o = Convenience.byteArrayToString (res[1]).toString().split("\n"); - for (let i = 0; i < o.length; i++) { - if (o[i].indexOf ("cpufreq-app") > -1) { - n = parseInt (o[i].trim().split(" ")[0]); - if (Number.isInteger(n) && n > 0) return n; + } + + launch_app(options) { + options = options || ''; + try { + GLib.spawn_command_line_async(`${this._appPath} ${options}`.trim()); + } catch (error) { + logError(error, '[cpufreq] Failed to launch application'); + } + } + + get app_running() { + try { + let [ok, stdout] = GLib.spawn_command_line_sync('ps -A'); + if (!ok) + return 0; + let lines = byteArrayToString(stdout).toString().split('\n'); + for (let line of lines) { + if (line.includes('cpufreq-app')) { + let pid = parseInt(line.trim().split(' ')[0]); + if (Number.isInteger(pid) && pid > 0) + return pid; + } } + } catch (error) { + // Ignore; best-effort check only. } return 0; - }, + } - get_title: function (text) { - if (!text) return title_text; - let metrics = JSON.parse (text), s = "", f = 0, units; - if (frequency_show) { + get_title(text) { + if (!text) + return this._titleText; + + let metrics = JSON.parse(text); + let s = ''; + let f = 0; + + if (this._frequencyShow) { f = metrics.frequency_maximum; - if (f) if (f < 1000000) { - units = " \u3392"; - s = (f / 1000).toFixed(0).toString (); - } else { - units = "\u3393"; - s = (f / 1000000).toFixed(2).toString (); + if (f) { + let units; + if (f < 1000000) { + units = ' ㎒'; + s = (f / 1000).toFixed(0).toString(); + } else { + units = '㎓'; + s = (f / 1000000).toFixed(2).toString(); + } + if (this._unitsShow) + s += units; } - if (units_show) s += units; } - if (governor_show && metrics.governor) { - s += " " + this.get_governor_symbolyc (metrics.governor); - } - if (load_show && Number.isInteger (metrics.state)) - s += ' ' + this.get_state_symbolyc (metrics.state); - if (label_show) s += ' ' + label_text; - if (s) title_text = s.trim (); - else title_text = label_text; - - if (color_show && Number.isInteger (metrics.state)) { - s = this.get_stylestring (metrics.state); - } else title_style = ""; - if (s != title_style) { - title_style = s; - this.statusLabel.style = title_style; + + if (this._governorShow && metrics.governor) + s += ` ${this.get_governor_symbolyc(metrics.governor)}`; + + if (this._loadShow && Number.isInteger(metrics.state)) + s += ` ${this.get_state_symbolyc(metrics.state)}`; + + if (this._labelShow) + s += ` ${this._labelText}`; + + if (s) + this._titleText = s.trim(); + else + this._titleText = this._labelText; + + if (this._colorShow && Number.isInteger(metrics.state)) + s = this.get_stylestring(metrics.state); + else + this._titleStyle = ''; + + if (s !== this._titleStyle) { + this._titleStyle = s; + this._statusLabel.style = this._titleStyle; } - return title_text; - }, - get_governor_symbolyc: function (name) { + return this._titleText; + } + + get_governor_symbolyc(name) { let g = name; - if (g == "mixed") g = "\u25cd"; - else if (g == "powersave") g = "\uf06c"; - else if (g == "performance") g = "\uf197"; - else if (g == "ondemand") g = "\uf0e7"; - else if (g == "conservative") g = "\ue976"; - else if (g == "schedutil") g = "\ue953"; - else if (g == "userspace") g = "\uf007"; - else g = "\uf0e7"; + if (g === 'mixed') + g = '◍'; + else if (g === 'powersave') + g = ''; + else if (g === 'performance') + g = ''; + else if (g === 'ondemand') + g = ''; + else if (g === 'conservative') + g = ''; + else if (g === 'schedutil') + g = ''; + else if (g === 'userspace') + g = ''; + else + g = ''; return g; - }, + } - get_state_symbolyc: function (state) { - let g = "☺"; - if (state == 1) g = ""; - else if (state == 2) g = "☹"; + get_state_symbolyc(state) { + let g = '☺'; + if (state === 1) + g = ''; + else if (state === 2) + g = '☹'; return g; - }, + } - get_stylestring: function (state) { + get_stylestring(state) { let s; - if (color_show_custom) state += 3; + if (this._colorShowCustom) + state += 3; + switch (state) { - case 0: - s = "color:" + color_show_default_normal + ";"; - break; - case 1: - s = "color:" + color_show_default_warning + ";"; - break; - case 2: - s = "color:" + color_show_default_critical + ";"; - break; - case 3: - s = "color:" + color_show_custom_normal + ";"; - break; - case 4: - s = "color:" + color_show_custom_warning + ";"; - break; - case 5: - s = "color:" + color_show_custom_critical + ";"; - break; - default: - s = ""; + case 0: + s = `color:${this._colorShowDefaultNormal};`; + break; + case 1: + s = `color:${this._colorShowDefaultWarning};`; + break; + case 2: + s = `color:${this._colorShowDefaultCritical};`; + break; + case 3: + s = `color:${this._colorShowCustomNormal};`; + break; + case 4: + s = `color:${this._colorShowCustomWarning};`; + break; + case 5: + s = `color:${this._colorShowCustomCritical};`; + break; + default: + s = ''; } + return s; - }, + } - add_event: function () { - this.remove_proxy (); - if (monitor_timeout > 0) { - if (!GLib.spawn_command_line_async (EXTENSIONDIR + "/cpufreq-service")) { - //error ("Unable to start cpufreq service..."); - return; - } - this.proxy = new CpufreqServiceProxy (Gio.DBus.session, BUS_NAME, OBJECT_PATH, (proxy, e) => { - if (e) { - error (e.message); - return; + add_event() { + this.remove_proxy(); + + if (this._monitorTimeout > 0) { + // Only try to spawn the service if it isn't already running. + // If spawning fails (already running, missing binary, etc.) still attempt + // to connect to the existing DBus name. + if (!sessionBusNameHasOwner(BUS_NAME)) { + try { + GLib.spawn_command_line_async(`${this._extensionDir}/cpufreq-service`); + } catch (error) { + logError(error, '[cpufreq] Unable to start cpufreq-service'); } - event = this.proxy.connectSignal ('MonitorEvent', (o, s, metrics) => { - if (metrics) this.statusLabel.set_text (this.get_title (metrics.toString ())); - }); - event_style = this.proxy.connectSignal ('StyleChanged', (o, s, style) => { - if (style) { - title_style = style.toString (); - this.statusLabel.style = title_style; + } + + this._proxy = new CpufreqServiceProxy( + Gio.DBus.session, + BUS_NAME, + OBJECT_PATH, + (proxy, error) => { + if (error) { + logError(error, '[cpufreq] DBus proxy error'); + return; } - }); - }); - } - monitor_event = 0; - // cpufreq-service should stop auto on disabled monitors - //else GLib.spawn_command_line_async ("killall cpufreq-service"); - }, - - remove_proxy: function () { - if (this.proxy) { - if (event) this.proxy.disconnectSignal (event); - if (event_style) this.proxy.disconnectSignal (event_style); - delete this.proxy; + + this._event = this._proxy.connectSignal('MonitorEvent', (_o, _s, metrics) => { + if (metrics) + this._statusLabel.set_text(this.get_title(metrics.toString())); + }); + + this._eventStyle = this._proxy.connectSignal('StyleChanged', (_o, _s, style) => { + if (!style) + return; + this._titleStyle = style.toString(); + this._statusLabel.style = this._titleStyle; + }); + } + ); } - this.proxy = null; - event = 0; - event_style = 0; - }, - - remove_events: function () { - this.remove_proxy (); - if (settingsID) this.settings.disconnect (settingsID); - if (powerID) this.power.disconnect (powerID); - if (monitor_event) GLib.source_remove (monitor_event); - event = 0; monitor_event = 0; - settingsID = 0; powerID = 0; - //GLib.spawn_command_line_async ("killall cpufreq-service"); - }, - - show_splash: function () { - let monitor = Main.layoutManager.focusMonitor; - let height = monitor.height < monitor.width ? monitor.height : monitor.width; - let width = 512 * height / 1200; - if (!this.splash) - this.splash = Gio.icon_new_for_string (EXTENSIONDIR + "/data/splash.svg"); - let splash = new St.Icon ({gicon: this.splash, icon_size: width}); - Main.uiGroup.add_actor (splash); - - splash.set_position (Math.floor (monitor.width / 2 - splash.width / 2), - Math.floor (monitor.height / 2 - splash.height / 2)); - - if (splash.ease) splash.ease ({ - opacity: 20, mode: 8, duration: 1200, - onComplete: () => { remove_actor (splash)} - }); else GLib.timeout_add (0, 1200, () => { return remove_actor (splash)}); + + this._monitorEventId = 0; + return GLib.SOURCE_REMOVE; } -}; - -let FrequencyIndicator = null; - -try { - FrequencyIndicator = GObject.registerClass({}, class FrequencyIndicator extends PanelMenu.Button { - _init() { - super._init (0.0, "CPU Frequency Indicator", false); - - this.on_settings = CpuFreq.on_settings.bind (this); - this.on_power_state = CpuFreq.on_power_state.bind (this); - this.unschedule_profile = CpuFreq.unschedule_profile.bind (this); - this.schedule_profile = CpuFreq.schedule_profile.bind (this); - this.launch_app = CpuFreq.launch_app.bind (this); - this.get_title = CpuFreq.get_title.bind (this); - this.get_governor_symbolyc = CpuFreq.get_governor_symbolyc.bind (this); - this.get_state_symbolyc = CpuFreq.get_state_symbolyc.bind (this); - this.get_stylestring = CpuFreq.get_stylestring.bind (this); - this.add_event = CpuFreq.add_event.bind (this); - this.remove_proxy = CpuFreq.remove_proxy.bind (this); - this.remove_events = CpuFreq.remove_events.bind (this); - this.show_splash = CpuFreq.show_splash.bind (this); - - CpuFreq.init.bind (this) (); - } - }); -} catch (error) { - log ('Gnome Shell version < 40!', error); - - FrequencyIndicator = new Lang.Class({ - Name: 'CpuFreq', - Extends: PanelMenu.Button, - - _init: function() { - this.parent (0.0, "CPU Frequency Indicator", false); - - this.on_settings = CpuFreq.on_settings.bind (this); - this.on_power_state = CpuFreq.on_power_state.bind (this); - this.unschedule_profile = CpuFreq.unschedule_profile.bind (this); - this.schedule_profile = CpuFreq.schedule_profile.bind (this); - this.launch_app = CpuFreq.launch_app.bind (this); - this.get_title = CpuFreq.get_title.bind (this); - this.get_governor_symbolyc = CpuFreq.get_governor_symbolyc.bind (this); - this.get_state_symbolyc = CpuFreq.get_state_symbolyc.bind (this); - this.get_stylestring = CpuFreq.get_stylestring.bind (this); - this.add_event = CpuFreq.add_event.bind (this); - this.remove_proxy = CpuFreq.remove_proxy.bind (this); - this.remove_events = CpuFreq.remove_events.bind (this); - this.show_splash = CpuFreq.show_splash.bind (this); - - CpuFreq.init.bind (this) (); + + remove_proxy() { + if (this._proxy) { + if (this._event) + this._proxy.disconnectSignal(this._event); + if (this._eventStyle) + this._proxy.disconnectSignal(this._eventStyle); } - }); -} -function remove_actor (o) { - Main.uiGroup.remove_actor (o); - o.destroy (); - return false; -} + this._proxy = null; + this._event = 0; + this._eventStyle = 0; + } -function show_notify (message, style) { - //var text = new St.Label ({text: message, style_class: style?style:'cpufreq-notify'}); - var text = new St.Label ({text: message, style_class: "modal-dialog audio-selection-content restart-message"}); - text.opacity = 255; - Main.uiGroup.add_actor (text); + remove_events() { + this.remove_proxy(); - text.set_position (Math.floor (Main.layoutManager.primaryMonitor.width / 2 - text.width / 2), - Math.floor (Main.layoutManager.primaryMonitor.height / 2 - text.height / 2)); + if (this._settingsChangedId) + this._settings.disconnect(this._settingsChangedId); + this._settingsChangedId = 0; - GLib.timeout_add (0, 1200, () => { return remove_actor (text)}); -} + if (this._powerChangedId) + this._power?.disconnect(this._powerChangedId); + this._powerChangedId = 0; -function show_warn (message) { - show_notify (message, "warn-label"); -} + if (this._monitorEventId) + GLib.source_remove(this._monitorEventId); + this._monitorEventId = 0; -let monitor; -Logger.init (Logger.LEVEL.ERROR, true); + if (this._scheduleId) + this.unschedule_profile(); + } -function info (msg) { - Logger.info ("extension", msg); -} + show_splash() { + let monitor = Main.layoutManager.focusMonitor ?? Main.layoutManager.primaryMonitor; + if (!monitor && Main.layoutManager.monitors?.length) + monitor = Main.layoutManager.monitors[0]; + if (!monitor) + return; -function debug (msg) { - Logger.debug ("extension", msg); -} + let height = monitor.height < monitor.width ? monitor.height : monitor.width; + let width = 512 * height / 1200; -function error (msg) { - Logger.error ("extension", msg); -} + if (!this._splash) + this._splash = Gio.icon_new_for_string(`${this._extensionDir}/data/splash.svg`); -function init () { -} + let splash = new St.Icon({gicon: this._splash, icon_size: width}); + addChild(getUiGroup(), splash); -function enable () { - monitor = new FrequencyIndicator (); - Main.panel.addToStatusArea ('cpufreq-indicator', monitor); -} + splash.set_position( + Math.floor(monitor.width / 2 - splash.width / 2), + Math.floor(monitor.height / 2 - splash.height / 2) + ); -function disable () { - monitor.remove_events (); - monitor.destroy (); - monitor = null; + if (splash.ease) { + splash.ease({ + opacity: 20, + mode: 8, + duration: 1200, + onComplete: () => removeActor(splash), + }); + } else { + GLib.timeout_add(GLib.PRIORITY_DEFAULT, 1200, () => removeActor(splash)); + } + } +}); + +export default class CPUFreqExtension extends Extension { + enable() { + this._indicator = new FrequencyIndicator(this); + Main.panel.addToStatusArea('cpufreq-indicator', this._indicator); + } + + disable() { + this._indicator?.remove_events(); + this._indicator?.destroy(); + this._indicator = null; + } } diff --git a/metadata.json b/metadata.json index 707b9be..fafc20b 100644 --- a/metadata.json +++ b/metadata.json @@ -3,7 +3,7 @@ "name": "cpufreq", "settings-schema": "org.gnome.shell.extensions.cpufreq", "shell-version": [ - "3.14","3.16","3.18","3.20","3.22","3.24","3.26","3.28","3.30","3.32","3.34","3.36","3.38","40","41","42","43","44" + "45","46","47","48","49" ], "url": "https://github.com/konkor/cpufreq", "uuid": "cpufreq@konkor", diff --git a/metadata.json.in b/metadata.json.in index 99fe4d7..b323019 100644 --- a/metadata.json.in +++ b/metadata.json.in @@ -3,7 +3,7 @@ "name": "cpufreq", "settings-schema": "org.gnome.shell.extensions.cpufreq", "shell-version": [ - "3.14","3.16","3.18","3.20","3.22","3.24","3.26","3.28","3.30","3.32","3.34","3.36","3.38","40","41","42","43","44" + "45","46","47","48","49" ], "url": "https://github.com/konkor/cpufreq", "uuid": "cpufreq@konkor", diff --git a/prefs.js b/prefs.js index b6f366d..606e36b 100644 --- a/prefs.js +++ b/prefs.js @@ -1,6 +1,6 @@ /* * This is a part of CPUFreq Manager - * Copyright (C) 2016-2019 konkor + * Copyright (C) 2016-2025 konkor * * Free Software Foundation, either version 3 of the License, or * (at your option) any later version. @@ -8,543 +8,32 @@ * with this program. If not, see . */ -const Lang = imports.lang; -const GObject = imports.gi.GObject; -const GLib = imports.gi.GLib; -const Gio = imports.gi.Gio; -const Gtk = imports.gi.Gtk; -const Gdk = imports.gi.Gdk; +import GLib from 'gi://GLib'; -var Format = imports.format; -String.prototype.format = Format.format; +import {ExtensionPreferences} from 'resource:///org/gnome/shell/extensions/prefs.js'; -const APPDIR = get_appdir (); -imports.searchPath.unshift(APPDIR); -const Logger = imports.common.Logger; - -const SAVE_SETTINGS_KEY = 'save-settings'; -const NOTIFY_KEY = 'notifications'; -const DARK_THEME_KEY = 'dark-theme'; -const PROFILES_KEY = 'profiles'; -const EPROFILES_KEY = 'event-profiles'; -const MONITOR_KEY = 'monitor'; -const LABEL_KEY = 'label'; -const LABEL_SHOW_KEY = 'label-show'; -const UNITS_SHOW_KEY = 'units-show'; -const LOAD_SHOW_KEY = 'load-show'; -const GOVS_SHOW_KEY = 'governors-show'; -const FREQ_SHOW_KEY = 'frequency-show'; - -const COLOR_SHOW_KEY = 'color-show'; -const COLOR_SHOW_CUSTOM_KEY = 'color-show-custom'; -const COLOR_SHOW_CUSTOM_NORMAL_KEY = 'color-show-custom-normal'; -const COLOR_SHOW_CUSTOM_WARNING_KEY = 'color-show-custom-warning'; -const COLOR_SHOW_CUSTOM_CRITICAL_KEY = 'color-show-custom-critical'; - -const Gettext = imports.gettext.domain ('org-konkor-cpufreq'); -const _ = Gettext.gettext; - -const EXTENSIONDIR = getCurrentFile ()[1]; -imports.searchPath.unshift (EXTENSIONDIR); -const Convenience = imports.convenience; - -var EventType = { -CHARGING: 0, -DISCHARGING: 1 -}; - -const suggestions = ["☃","⚡","㎒","㎓","","","","","","","","CPU"]; - -let save = false; -let dark = false; -let profiles = []; -let monitor_timeout = 500; -let label_text = "\u269b"; -let label_show = false; -let frequency_show = true; -let governor_show = false; -let load_show = false; -let units_show = true; -let system_notifications = false; - -let auto_profiles = [ - {name:_("Battery"), guid:"battery"}, - {name:_("Balanced"), guid:"balanced"}, - {name:_("High Performance"), guid:"performance"} -]; - -let color_show = false; -let color_show_custom = false; -let color_show_custom_normal = '#ebebeb'; -let color_show_custom_warning = '#ebebeb'; -let color_show_custom_critical = '#ff0000'; - -let eprofiles = [ - {percent:0, event:EventType.CHARGING, guid:""}, - {percent:100, event:EventType.DISCHARGING, guid:""} -]; - -let settings = false; - -var CPUFreqPreferences = new Lang.Class({ - Name: 'CPUFreqPreferences', - - _init: function () { - this.parent (0.0, "CPUFreq Preferences", false); - let label, s; - - settings = Convenience.getSettings (); - save = settings.get_boolean (SAVE_SETTINGS_KEY); - dark = settings.get_boolean (DARK_THEME_KEY); - monitor_timeout = settings.get_int (MONITOR_KEY); - label_text = settings.get_string (LABEL_KEY); - label_show = settings.get_boolean (LABEL_SHOW_KEY); - load_show = settings.get_boolean (LOAD_SHOW_KEY); - governor_show = settings.get_boolean (GOVS_SHOW_KEY); - frequency_show = settings.get_boolean (FREQ_SHOW_KEY); - units_show = settings.get_boolean (UNITS_SHOW_KEY); - system_notifications = settings.get_boolean (NOTIFY_KEY); - - color_show = settings.get_boolean (COLOR_SHOW_KEY); - color_show_custom = settings.get_boolean (COLOR_SHOW_CUSTOM_KEY); - color_show_custom_normal = settings.get_string (COLOR_SHOW_CUSTOM_NORMAL_KEY); - color_show_custom_warning = settings.get_string (COLOR_SHOW_CUSTOM_WARNING_KEY); - color_show_custom_critical = settings.get_string (COLOR_SHOW_CUSTOM_CRITICAL_KEY); - - s = settings.get_string (EPROFILES_KEY); - if (s) try { - eprofiles = JSON.parse (s); - } catch (e) { - Logger.error ("%s\nPARSING DATA: %s".format (e.message, s)); - } - s = settings.get_string (PROFILES_KEY); - if (s) try { - profiles = JSON.parse (s); - } catch (e) { - Logger.error ("%s\nPARSING DATA: %s".format (e.message, s)); - } - - this.notebook = new Gtk.Notebook ({expand:true}); - let cssp = get_css_provider (); - if (cssp) { - Gtk.StyleContext.add_provider_for_screen ( - this.notebook.get_screen(), cssp, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); - } - - this.general = new PageGeneralCPUFreq (); - this.notebook.add (this.general); - label = new Gtk.Label ({label: _("General")}); - this.notebook.set_tab_label (this.general, label); - - this.monitor_page = new PageMonitorCPUFreq (); - this.notebook.add (this.monitor_page); - label = new Gtk.Label ({label: _("Monitor")}); - this.notebook.set_tab_label (this.monitor_page, label); - - this.power = new PagePowerCPUFreq (); - this.notebook.add (this.power); - label = new Gtk.Label ({label: _("Power Events")}); - this.notebook.set_tab_label (this.power, label); - - this.notebook.show_all (); - } -}); - - -var PageGeneralCPUFreq = new Lang.Class({ - Name: 'PageGeneralCPUFreq', - Extends: Gtk.Box, - - _init: function () { - this.parent ({orientation:Gtk.Orientation.VERTICAL, margin:6}); - this.border_width = 6; - - this.add (new Gtk.Label ({label: "" + _("System") + (""), use_markup:true, xalign:0, margin_top:8})); - this.cb_startup = Gtk.CheckButton.new_with_label (_("Remember settings")); - this.cb_startup.tooltip_text = _("Check to restore settings on startup"); - this.cb_startup.margin = 6; - this.add (this.cb_startup); - this.cb_startup.active = save; - this.cb_startup.connect ('toggled', () => { - save = this.cb_startup.active; - settings.set_boolean (SAVE_SETTINGS_KEY, save); - }); - - this.cb_notify = Gtk.CheckButton.new_with_label (_("Notifications")); - this.cb_notify.tooltip_text = _("Show System Notifications on Critical States"); - this.cb_notify.margin = 6; - this.add (this.cb_notify); - this.cb_notify.active = system_notifications; - this.cb_notify.connect ('toggled', () => { - system_notifications = this.cb_notify.active; - settings.set_boolean (NOTIFY_KEY, system_notifications); - }); - - this.add (new Gtk.Label ({label: _("Extension"), use_markup:true, xalign:0, margin_top:12})); - this.cb_mode = Gtk.CheckButton.new_with_label (_("Extension mode")); - this.cb_mode.tooltip_text = _("Run main application without window decorations"); - this.cb_mode.margin = 6; - this.add (this.cb_mode); - this.cb_mode.active = settings.get_boolean ("extension-mode");; - this.cb_mode.connect ('toggled', () => { - settings.set_boolean ("extension-mode", this.cb_mode.active); - }); - this.cb_splash = Gtk.CheckButton.new_with_label (_("Splash Screen")); - this.cb_splash.tooltip_text = _("Show a splash screen on selecting main application"); - this.cb_splash.margin = 6; - this.add (this.cb_splash); - this.cb_splash.active = settings.get_boolean ("show-splash");; - this.cb_splash.connect ('toggled', () => { - settings.set_boolean ("show-splash", this.cb_splash.active); - }); - - this.add (new Gtk.Label ({label: _("User Interface"), use_markup:true, xalign:0, margin_top:12})); - this.cb_dark = Gtk.CheckButton.new_with_label (_("Dark theme")); - this.cb_dark.tooltip_text = _("Prefer dark theme"); - this.cb_dark.margin = 6; - this.add (this.cb_dark); - this.cb_dark.active = dark; - this.cb_dark.connect ('toggled', () => { - dark = this.cb_dark.active; - settings.set_boolean (DARK_THEME_KEY, dark); - }); - - this.show_all (); - } -}); - -var PageMonitorCPUFreq = new Lang.Class({ - Name: 'PageMonitorCPUFreq', - Extends: Gtk.Box, - - _init: function () { - this.parent ({orientation:Gtk.Orientation.VERTICAL, margin:6}); - let id = 0, i = 0, rb; - this.border_width = 6; - - let hbox = new Gtk.Box ({orientation:Gtk.Orientation.HORIZONTAL, margin:8}); - this.pack_start (hbox, false, false, 0); - hbox.add (new Gtk.Label ({label: _("Monitoring Interval (ms)")})); - this.timeout = Gtk.SpinButton.new_with_range (0, 1000000, 50); - this.timeout.tooltip_text = _("1000ms - default, 0 - disable"); - this.timeout.value = monitor_timeout; - this.timeout.connect ('value_changed', () => { - monitor_timeout = this.timeout.value; - settings.set_int (MONITOR_KEY, monitor_timeout); - }); - hbox.pack_end (this.timeout, false, false, 0); - - hbox = new Gtk.Box ({orientation:Gtk.Orientation.HORIZONTAL, margin:6, margin_left:32}); - this.pack_start (hbox, false, false, 0); - hbox.pack_start (new Gtk.Label ({label: _("Show"), xalign:0.0}), false, false, 0); - - let cb_units = Gtk.CheckButton.new_with_label (_("Frequency")); - cb_units.tooltip_text = _("Monitor frequency"); - cb_units.margin_start = 32; - cb_units.active = frequency_show; - hbox.pack_start (cb_units, true, true, 8); - cb_units.connect ('toggled', (o) => { - frequency_show = o.active; - settings.set_boolean (FREQ_SHOW_KEY, frequency_show); - }); - - cb_units = Gtk.CheckButton.new_with_label (_("Governors")); - cb_units.tooltip_text = _("Monitor governors"); - cb_units.active = governor_show; - hbox.pack_start (cb_units, true, true, 8); - cb_units.connect ('toggled', (o) => { - governor_show = o.active; - settings.set_boolean (GOVS_SHOW_KEY, governor_show); - }); - - cb_units = Gtk.CheckButton.new_with_label (_("Loading")); - cb_units.tooltip_text = _("Monitor system loading"); - cb_units.active = load_show; - hbox.pack_start (cb_units, true, true, 8); - cb_units.connect ('toggled', (o) => { - load_show = o.active; - settings.set_boolean (LOAD_SHOW_KEY, load_show); - }); - - this.cb_units = Gtk.CheckButton.new_with_label (_("Show Measurement Units")); - this.cb_units.tooltip_text = _("Show measurement units for frequencies"); - this.cb_units.margin = 6; - this.add (this.cb_units); - this.cb_units.active = units_show; - this.cb_units.connect ('toggled', (o) => { - units_show = o.active; - settings.set_boolean (UNITS_SHOW_KEY, units_show); - }); - - this.cb_label = Gtk.CheckButton.new_with_label (_("Show Custom Label")); - this.cb_label.tooltip_text = _("Always show the custom label"); - this.cb_label.margin = 6; - this.add (this.cb_label); - this.cb_label.active = label_show; - this.cb_label.connect ('toggled', (o) => { - label_show = o.active; - settings.set_boolean (LABEL_SHOW_KEY, label_show); - }); - - hbox = new Gtk.Box ({orientation:Gtk.Orientation.HORIZONTAL, margin:8}); - this.pack_start (hbox, false, false, 0); - hbox.add (new Gtk.Label ({label: _("Custom label when monitoring disabled")})); - this.store = new Gtk.ListStore (); - this.store.set_column_types ([GObject.TYPE_STRING]); - this.completion = new Gtk.EntryCompletion (); - this.completion.minimum_key_length = 0; - this.completion.set_model (this.store); - this.completion.set_text_column (0); - suggestions.forEach ( l => { - this.store.set (this.store.append (), [0], [l]); - }); - - this.label = new Gtk.Entry (); - this.label.set_completion (this.completion); - this.label.get_style_context().add_class ("cpufreq-text"); - this.label.tooltip_text = _("Label or just a symbol to show when monitor disabled"); - this.label.set_text (label_text); - this.label.connect ('changed', (o) => { - var s = o.text; - settings.set_string (LABEL_KEY, s); - }); - - hbox.pack_end (this.label, false, false, 0); - - hbox = new Gtk.Box ({orientation:Gtk.Orientation.HORIZONTAL, margin:6}); - this.pack_start (hbox, false, false, 0); - this.cb_color = Gtk.CheckButton.new_with_label (_("Use color")); - this.cb_color.tooltip_text = _("Colorful Monitor's title depending on warning state"); - this.cb_color.active = color_show; - hbox.pack_start (this.cb_color, true, true, 0); - this.cb_color.connect ('toggled', (o) => { - color_show = o.active; - settings.set_boolean (COLOR_SHOW_KEY, color_show); - this.colorbox.sensitive = color_show && color_show_custom; - }); - rb = Gtk.RadioButton.new_with_label_from_widget (null, _("Default colors")); - rb.active = !settings.get_boolean (COLOR_SHOW_CUSTOM_KEY); - rb.id = 0; - hbox.pack_start(rb, true, true, 8); - rb.connect ('toggled', (o) => { - color_show_custom = !o.active; - settings.set_boolean (COLOR_SHOW_CUSTOM_KEY, color_show_custom); - }); - rb = Gtk.RadioButton.new_with_label_from_widget (rb, _("Custom colors")); - rb.active = settings.get_boolean (COLOR_SHOW_CUSTOM_KEY); - rb.id = 1; - hbox.pack_start (rb, true, true, 8); - rb.connect ('toggled', (o) => { - color_show_custom = o.active; - settings.set_boolean (COLOR_SHOW_CUSTOM_KEY, color_show_custom); - this.colorbox.sensitive = color_show && color_show_custom; - }); - - this.colorbox = new Gtk.Box ({orientation:Gtk.Orientation.HORIZONTAL, margin:8}); - this.pack_start (this.colorbox, false, false, 0); - - this.colorbox.add (new Gtk.Label ({label: _("Normal")})); - let [ ,color] = Gdk.Color.parse (color_show_custom_normal); - this.color_normal = Gtk.ColorButton.new_with_color (color); - this.color_normal.connect ('color-set', (o) => { - settings.set_string (COLOR_SHOW_CUSTOM_NORMAL_KEY, this.color_string (o.rgba)); - }); - this.colorbox.pack_start (this.color_normal, true, false, 0); - - this.colorbox.add (new Gtk.Label ({label: _("Warning")})); - [ ,color] = Gdk.Color.parse (color_show_custom_warning); - this.color_warning = Gtk.ColorButton.new_with_color (color); - this.color_warning.connect ('color-set', (o) => { - settings.set_string (COLOR_SHOW_CUSTOM_WARNING_KEY, this.color_string (o.rgba)); - }); - this.colorbox.pack_start (this.color_warning, true, false, 0); - - this.colorbox.add (new Gtk.Label ({label: _("Critical")})); - [ ,color] = Gdk.Color.parse (color_show_custom_critical); - this.color_critical = Gtk.ColorButton.new_with_color (color); - this.color_critical.connect ('color-set', (o) => { - settings.set_string (COLOR_SHOW_CUSTOM_CRITICAL_KEY, this.color_string (o.rgba)); - }); - this.colorbox.pack_start (this.color_critical, true, false, 0); - this.colorbox.sensitive = color_show && color_show_custom; - - this.show_all (); - }, - - color_string: function (rgba) { - let s = "#%02x%02x%02x".format ( - this.scale_round (rgba.red), - this.scale_round (rgba.green), - this.scale_round (rgba.blue) - ); - return s; - }, - - scale_round: function (val) { - val = Math.floor (val * 255 + 0.5); - val = Math.max (val, 0); - val = Math.min (val, 255); - return val; - } -}); - -var PagePowerCPUFreq = new Lang.Class({ - Name: 'PagePowerCPUFreq', - Extends: Gtk.Box, - - _init: function () { - this.parent ({orientation:Gtk.Orientation.VERTICAL, margin:6}); - this.border_width = 6; - - this.unplug = new PowerProfile (eprofiles[EventType.DISCHARGING], - _("On Battery"), - _("You can set less than 100% of the battery level. ") + - _("It will do not apply \"Powersaving Profile\" immediately on the event. ") + - _("It could be helpful on temporary disconnections, issues with a power connector, etc.") - ); - this.add (this.unplug); - this.unplug.combo.connect ('changed', (o) => { - if (o.active == 0) - eprofiles[EventType.DISCHARGING].guid = ""; - else if (o.active <= auto_profiles.length) - eprofiles[EventType.DISCHARGING].guid = auto_profiles[o.active - 1].guid; - else - eprofiles[EventType.DISCHARGING].guid = profiles[o.active - auto_profiles.length - 1].guid; - //this.unplug.slider.set_value (100); - settings.set_string (EPROFILES_KEY, JSON.stringify (eprofiles)); - }); - this.unplug.slider.connect ('value_changed', (o) => { - eprofiles[EventType.DISCHARGING].percent = Math.round (o.get_value ()); - settings.set_string (EPROFILES_KEY, JSON.stringify (eprofiles)); - }); - - this.show_all (); - } -}); - -var PowerProfile = new Lang.Class({ - Name: 'PowerProfile', - Extends: Gtk.Box, - - _init: function (profile, text, tooltip) { - this.parent ({orientation:Gtk.Orientation.VERTICAL, margin:6}); - this.tooltip_text = tooltip; - this.border_width = 6; - let id = 0, i = 1; - - let hbox = new Gtk.Box ({orientation:Gtk.Orientation.HORIZONTAL}); - this.pack_start (hbox, false, false, 0); - hbox.add (new Gtk.Label ({label:""+text+"",use_markup:true,xalign:0})); - this.combo = new Gtk.ComboBoxText (); - this.combo.tooltip_text = _("Power Profile"); - this.combo.append_text (_("do nothing")); - auto_profiles.forEach (s => { - this.combo.append_text (s.name); - if (s.guid == profile.guid) id = i; - i++; - }); - profiles.forEach (s => { - this.combo.append_text (s.name + " (user profile)"); - if (s.guid == profile.guid) id = i; - i++; - }); - this.combo.active = id; - hbox.pack_end (this.combo, false, false, 0); - - hbox = new Gtk.Box ({orientation:Gtk.Orientation.HORIZONTAL}); - hbox.margin_top = 8; - this.add (hbox); - this.label = new Gtk.Label ({label:_("Level"), use_markup:true, xalign:0}); - hbox.pack_start (this.label, true, true, 0); - this.info = new Gtk.Label ({label:""+profile.percent+"%", use_markup:true}); - hbox.pack_end (this.info, false, false, 0); - this.slider = Gtk.Scale.new_with_range (Gtk.Orientation.HORIZONTAL, 0, 100, 1); - this.slider.draw_value = false; - this.slider.set_value (profile.percent); - this.add (this.slider); - this.slider.connect('value_changed', (o) => { - this.update_info (Math.round (o.get_value ()).toString ()); - }); - - this.show_all (); - }, - - update_info: function (info) { - this.info.set_markup ("" + info + "%"); - } -}); - -const css_theme = " \ -.cpufreq-text { font-family: cpufreq, roboto, cantarell} \ -"; - -function get_css_provider () { - let cssp = new Gtk.CssProvider (); +export default class CPUFreqPreferences extends ExtensionPreferences { + fillPreferencesWindow(window) { try { - cssp.load_from_data (css_theme); - } catch (e) { - print (e); - cssp = null; - } - return cssp; -} - -function getCurrentFile () { - let stack = (new Error()).stack; - let stackLine = stack.split('\n')[1]; - if (!stackLine) - throw new Error ('Could not find current file'); - let match = new RegExp ('@(.+):\\d+').exec(stackLine); - if (!match) - throw new Error ('Could not find current file'); - let path = match[1]; - let file = Gio.File.new_for_path (path); - return [file.get_path(), file.get_parent().get_path(), file.get_basename()]; -} - -function debug (msg) { - if (msg) print ("[cpufreq][prefs] " + msg); -} - -function error (msg) { - log ("[cpufreq][prefs] (EE) " + msg); -} - -function init () { - Convenience.initTranslations (); -} - -function buildPrefsWidget () { - if (Gtk.MAJOR_VERSION > 3) { - GLib.spawn_command_line_async (APPDIR + "/cpufreq-preferences"); - let widget = new DumpWidget (); - return widget; - } - let widget = new CPUFreqPreferences (); - return widget.notebook; -} - -var DumpWidget = new Lang.Class({ - Name: 'DumpWidget', - Extends: Gtk.Box, - - _init: function () { - this.parent ({}); - /*this.connect ('realize', () => { - this.get_parent ().get_parent ().close (); - });*/ - } -}); - -function get_appdir () { - let s = getCurrentFile ()[1]; - if (GLib.file_test (s + "/prefs.js", GLib.FileTest.EXISTS)) return s; - s = GLib.get_home_dir () + "/.local/share/gnome-shell/extensions/cpufreq@konkor"; - if (GLib.file_test (s + "/prefs.js", GLib.FileTest.EXISTS)) return s; - s = "/usr/local/share/gnome-shell/extensions/cpufreq@konkor"; - if (GLib.file_test (s + "/prefs.js", GLib.FileTest.EXISTS)) return s; - s = "/usr/share/gnome-shell/extensions/cpufreq@konkor"; - if (GLib.file_test (s + "/prefs.js", GLib.FileTest.EXISTS)) return s; - throw "CPUFreq installation not found..."; - return s; + // CPUFreq's dedicated Gtk3 preferences UI is not compatible with GNOME Shell 45+ + // preferences hosting; open the manager app instead. + GLib.spawn_command_line_async(`${this.path}/cpufreq-application`); + } catch (error) { + logError(error, '[cpufreq] Failed to launch cpufreq-application'); + } + + // CPUFreq uses a dedicated preferences app; close the stub window. + GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, () => { + try { + window.close(); + } catch { + try { + window.destroy(); + } catch { + // ignore + } + } + return GLib.SOURCE_REMOVE; + }); + } } diff --git a/prefs_legacy.js b/prefs_legacy.js new file mode 100644 index 0000000..1028c94 --- /dev/null +++ b/prefs_legacy.js @@ -0,0 +1,559 @@ +/* + * Legacy GTK3 preferences implementation. + * + * This file exists for the standalone `cpufreq-preferences` app (Gtk3/GJS classic + * imports). GNOME Shell 45+ loads `prefs.js` as an ES module. + */ + +/* + * This is a part of CPUFreq Manager + * Copyright (C) 2016-2019 konkor + * + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +const Lang = imports.lang; +const GObject = imports.gi.GObject; +const GLib = imports.gi.GLib; +const Gio = imports.gi.Gio; +const Gtk = imports.gi.Gtk; +const Gdk = imports.gi.Gdk; + +var Format = imports.format; +String.prototype.format = Format.format; + +const APPDIR = get_appdir (); +imports.searchPath.unshift(APPDIR); +const Logger = imports.common.Logger; + +const SAVE_SETTINGS_KEY = 'save-settings'; +const NOTIFY_KEY = 'notifications'; +const DARK_THEME_KEY = 'dark-theme'; +const PROFILES_KEY = 'profiles'; +const EPROFILES_KEY = 'event-profiles'; +const MONITOR_KEY = 'monitor'; +const LABEL_KEY = 'label'; +const LABEL_SHOW_KEY = 'label-show'; +const UNITS_SHOW_KEY = 'units-show'; +const LOAD_SHOW_KEY = 'load-show'; +const GOVS_SHOW_KEY = 'governors-show'; +const FREQ_SHOW_KEY = 'frequency-show'; + +const COLOR_SHOW_KEY = 'color-show'; +const COLOR_SHOW_CUSTOM_KEY = 'color-show-custom'; +const COLOR_SHOW_CUSTOM_NORMAL_KEY = 'color-show-custom-normal'; +const COLOR_SHOW_CUSTOM_WARNING_KEY = 'color-show-custom-warning'; +const COLOR_SHOW_CUSTOM_CRITICAL_KEY = 'color-show-custom-critical'; + +const Gettext = imports.gettext.domain ('org-konkor-cpufreq'); +const _ = Gettext.gettext; + +const EXTENSIONDIR = getCurrentFile ()[1]; +imports.searchPath.unshift (EXTENSIONDIR); +const Convenience = imports.convenience; + +var EventType = { +CHARGING: 0, +DISCHARGING: 1 +}; + +const suggestions = ["☃","⚡","㎒","㎓","","","","","","","","CPU"]; + +let save = false; +let dark = false; +let profiles = []; +let monitor_timeout = 500; +let label_text = "\u269b"; +let label_show = false; +let frequency_show = true; +let governor_show = false; +let load_show = false; +let units_show = true; +let system_notifications = false; + +let auto_profiles = [ + {name:_("Battery"), guid:"battery"}, + {name:_("Balanced"), guid:"balanced"}, + {name:_("High Performance"), guid:"performance"} +]; + +let color_show = false; +let color_show_custom = false; +let color_show_custom_normal = '#ebebeb'; +let color_show_custom_warning = '#ebebeb'; +let color_show_custom_critical = '#ff0000'; + +let eprofiles = [ + {percent:0, event:EventType.CHARGING, guid:""}, + {percent:100, event:EventType.DISCHARGING, guid:""} +]; + +let settings = false; + +var CPUFreqPreferences = new Lang.Class({ + Name: 'CPUFreqPreferences', + + _init: function () { + this.parent (0.0, "CPUFreq Preferences", false); + let label, s; + + settings = Convenience.getSettings (); + save = settings.get_boolean (SAVE_SETTINGS_KEY); + dark = settings.get_boolean (DARK_THEME_KEY); + monitor_timeout = settings.get_int (MONITOR_KEY); + label_text = settings.get_string (LABEL_KEY); + label_show = settings.get_boolean (LABEL_SHOW_KEY); + load_show = settings.get_boolean (LOAD_SHOW_KEY); + governor_show = settings.get_boolean (GOVS_SHOW_KEY); + frequency_show = settings.get_boolean (FREQ_SHOW_KEY); + units_show = settings.get_boolean (UNITS_SHOW_KEY); + system_notifications = settings.get_boolean (NOTIFY_KEY); + + color_show = settings.get_boolean (COLOR_SHOW_KEY); + color_show_custom = settings.get_boolean (COLOR_SHOW_CUSTOM_KEY); + color_show_custom_normal = settings.get_string (COLOR_SHOW_CUSTOM_NORMAL_KEY); + color_show_custom_warning = settings.get_string (COLOR_SHOW_CUSTOM_WARNING_KEY); + color_show_custom_critical = settings.get_string (COLOR_SHOW_CUSTOM_CRITICAL_KEY); + + s = settings.get_string (EPROFILES_KEY); + if (s) try { + eprofiles = JSON.parse (s); + } catch (e) { + Logger.error ("%s\nPARSING DATA: %s".format (e.message, s)); + } + s = settings.get_string (PROFILES_KEY); + if (s) try { + profiles = JSON.parse (s); + } catch (e) { + Logger.error ("%s\nPARSING DATA: %s".format (e.message, s)); + } + + this.notebook = new Gtk.Notebook ({expand:true}); + let cssp = get_css_provider (); + if (cssp) { + Gtk.StyleContext.add_provider_for_screen ( + this.notebook.get_screen(), cssp, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); + } + + this.general = new PageGeneralCPUFreq (); + this.notebook.add (this.general); + label = new Gtk.Label ({label: _("General")}); + this.notebook.set_tab_label (this.general, label); + + this.monitor_page = new PageMonitorCPUFreq (); + this.notebook.add (this.monitor_page); + label = new Gtk.Label ({label: _("Monitor")}); + this.notebook.set_tab_label (this.monitor_page, label); + + this.power = new PagePowerCPUFreq (); + this.notebook.add (this.power); + label = new Gtk.Label ({label: _("Power Events")}); + this.notebook.set_tab_label (this.power, label); + + this.notebook.show_all (); + } +}); + + +var PageGeneralCPUFreq = new Lang.Class({ + Name: 'PageGeneralCPUFreq', + Extends: Gtk.Box, + + _init: function () { + this.parent ({orientation:Gtk.Orientation.VERTICAL, margin:6}); + this.border_width = 6; + + this.add (new Gtk.Label ({label: "" + _("System") + (""), use_markup:true, xalign:0, margin_top:8})); + this.cb_startup = Gtk.CheckButton.new_with_label (_("Remember settings")); + this.cb_startup.tooltip_text = _("Check to restore settings on startup"); + this.cb_startup.margin = 6; + this.add (this.cb_startup); + this.cb_startup.active = save; + this.cb_startup.connect ('toggled', () => { + save = this.cb_startup.active; + settings.set_boolean (SAVE_SETTINGS_KEY, save); + }); + + this.cb_notify = Gtk.CheckButton.new_with_label (_("Notifications")); + this.cb_notify.tooltip_text = _("Show System Notifications on Critical States"); + this.cb_notify.margin = 6; + this.add (this.cb_notify); + this.cb_notify.active = system_notifications; + this.cb_notify.connect ('toggled', () => { + system_notifications = this.cb_notify.active; + settings.set_boolean (NOTIFY_KEY, system_notifications); + }); + + this.add (new Gtk.Label ({label: _("Extension"), use_markup:true, xalign:0, margin_top:12})); + this.cb_mode = Gtk.CheckButton.new_with_label (_("Extension mode")); + this.cb_mode.tooltip_text = _("Run main application without window decorations"); + this.cb_mode.margin = 6; + this.add (this.cb_mode); + this.cb_mode.active = settings.get_boolean ("extension-mode");; + this.cb_mode.connect ('toggled', () => { + settings.set_boolean ("extension-mode", this.cb_mode.active); + }); + this.cb_splash = Gtk.CheckButton.new_with_label (_("Splash Screen")); + this.cb_splash.tooltip_text = _("Show a splash screen on selecting main application"); + this.cb_splash.margin = 6; + this.add (this.cb_splash); + this.cb_splash.active = settings.get_boolean ("show-splash");; + this.cb_splash.connect ('toggled', () => { + settings.set_boolean ("show-splash", this.cb_splash.active); + }); + + + this.add (new Gtk.Label ({label: _("User Interface"), use_markup:true, xalign:0, margin_top:12})); + this.cb_dark = Gtk.CheckButton.new_with_label (_("Dark theme")); + this.cb_dark.tooltip_text = _("Prefer dark theme"); + this.cb_dark.margin = 6; + this.add (this.cb_dark); + this.cb_dark.active = dark; + this.cb_dark.connect ('toggled', () => { + dark = this.cb_dark.active; + settings.set_boolean (DARK_THEME_KEY, dark); + }); + + this.show_all (); + } +}); + +var PageMonitorCPUFreq = new Lang.Class({ + Name: 'PageMonitorCPUFreq', + Extends: Gtk.Box, + + _init: function () { + this.parent ({orientation:Gtk.Orientation.VERTICAL, margin:6}); + let id = 0, i = 0, rb; + this.border_width = 6; + + let hbox = new Gtk.Box ({orientation:Gtk.Orientation.HORIZONTAL, margin:8}); + this.pack_start (hbox, false, false, 0); + hbox.add (new Gtk.Label ({label: _("Monitoring Interval (ms)")})); + this.timeout = Gtk.SpinButton.new_with_range (0, 1000000, 50); + this.timeout.tooltip_text = _("1000ms - default, 0 - disable"); + this.timeout.value = monitor_timeout; + this.timeout.connect ('value_changed', () => { + monitor_timeout = this.timeout.value; + settings.set_int (MONITOR_KEY, monitor_timeout); + }); + hbox.pack_end (this.timeout, false, false, 0); + + hbox = new Gtk.Box ({orientation:Gtk.Orientation.HORIZONTAL, margin:6, margin_left:32}); + this.pack_start (hbox, false, false, 0); + hbox.pack_start (new Gtk.Label ({label: _("Show"), xalign:0.0}), false, false, 0); + + let cb_units = Gtk.CheckButton.new_with_label (_("Frequency")); + cb_units.tooltip_text = _("Monitor frequency"); + cb_units.margin_start = 32; + cb_units.active = frequency_show; + hbox.pack_start (cb_units, true, true, 8); + cb_units.connect ('toggled', (o) => { + frequency_show = o.active; + settings.set_boolean (FREQ_SHOW_KEY, frequency_show); + }); + + cb_units = Gtk.CheckButton.new_with_label (_("Governors")); + cb_units.tooltip_text = _("Monitor governors"); + cb_units.active = governor_show; + hbox.pack_start (cb_units, true, true, 8); + cb_units.connect ('toggled', (o) => { + governor_show = o.active; + settings.set_boolean (GOVS_SHOW_KEY, governor_show); + }); + + cb_units = Gtk.CheckButton.new_with_label (_("Loading")); + cb_units.tooltip_text = _("Monitor system loading"); + cb_units.active = load_show; + hbox.pack_start (cb_units, true, true, 8); + cb_units.connect ('toggled', (o) => { + load_show = o.active; + settings.set_boolean (LOAD_SHOW_KEY, load_show); + }); + + this.cb_units = Gtk.CheckButton.new_with_label (_("Show Measurement Units")); + this.cb_units.tooltip_text = _("Show measurement units for frequencies"); + this.cb_units.margin = 6; + this.add (this.cb_units); + this.cb_units.active = units_show; + this.cb_units.connect ('toggled', (o) => { + units_show = o.active; + settings.set_boolean (UNITS_SHOW_KEY, units_show); + }); + + this.cb_label = Gtk.CheckButton.new_with_label (_("Show Custom Label")); + this.cb_label.tooltip_text = _("Always show the custom label"); + this.cb_label.margin = 6; + this.add (this.cb_label); + this.cb_label.active = label_show; + this.cb_label.connect ('toggled', (o) => { + label_show = o.active; + settings.set_boolean (LABEL_SHOW_KEY, label_show); + }); + + hbox = new Gtk.Box ({orientation:Gtk.Orientation.HORIZONTAL, margin:8}); + this.pack_start (hbox, false, false, 0); + hbox.add (new Gtk.Label ({label: _("Custom label when monitoring disabled")})); + this.store = new Gtk.ListStore (); + this.store.set_column_types ([GObject.TYPE_STRING]); + this.completion = new Gtk.EntryCompletion (); + this.completion.minimum_key_length = 0; + this.completion.set_model (this.store); + this.completion.set_text_column (0); + suggestions.forEach ( l => { + this.store.set (this.store.append (), [0], [l]); + }); + + this.label = new Gtk.Entry (); + this.label.set_completion (this.completion); + this.label.get_style_context().add_class ("cpufreq-text"); + this.label.tooltip_text = _("Label or just a symbol to show when monitor disabled"); + this.label.set_text (label_text); + this.label.connect ('changed', (o) => { + var s = o.text; + settings.set_string (LABEL_KEY, s); + }); + + hbox.pack_end (this.label, false, false, 0); + + hbox = new Gtk.Box ({orientation:Gtk.Orientation.HORIZONTAL, margin:6}); + this.pack_start (hbox, false, false, 0); + this.cb_color = Gtk.CheckButton.new_with_label (_("Use color")); + this.cb_color.tooltip_text = _("Colorful Monitor's title depending on warning state"); + this.cb_color.active = color_show; + hbox.pack_start (this.cb_color, true, true, 0); + this.cb_color.connect ('toggled', (o) => { + color_show = o.active; + settings.set_boolean (COLOR_SHOW_KEY, color_show); + this.colorbox.sensitive = color_show && color_show_custom; + }); + rb = Gtk.RadioButton.new_with_label_from_widget (null, _("Default colors")); + rb.active = !settings.get_boolean (COLOR_SHOW_CUSTOM_KEY); + rb.id = 0; + hbox.pack_start(rb, true, true, 8); + rb.connect ('toggled', (o) => { + color_show_custom = !o.active; + settings.set_boolean (COLOR_SHOW_CUSTOM_KEY, color_show_custom); + }); + rb = Gtk.RadioButton.new_with_label_from_widget (rb, _("Custom colors")); + rb.active = settings.get_boolean (COLOR_SHOW_CUSTOM_KEY); + rb.id = 1; + hbox.pack_start (rb, true, true, 8); + rb.connect ('toggled', (o) => { + color_show_custom = o.active; + settings.set_boolean (COLOR_SHOW_CUSTOM_KEY, color_show_custom); + this.colorbox.sensitive = color_show && color_show_custom; + }); + + this.colorbox = new Gtk.Box ({orientation:Gtk.Orientation.HORIZONTAL, margin:8}); + this.pack_start (this.colorbox, false, false, 0); + + this.colorbox.add (new Gtk.Label ({label: _("Normal")})); + let [ ,color] = Gdk.Color.parse (color_show_custom_normal); + this.color_normal = Gtk.ColorButton.new_with_color (color); + this.color_normal.connect ('color-set', (o) => { + settings.set_string (COLOR_SHOW_CUSTOM_NORMAL_KEY, this.color_string (o.rgba)); + }); + this.colorbox.pack_start (this.color_normal, true, false, 0); + + this.colorbox.add (new Gtk.Label ({label: _("Warning")})); + [ ,color] = Gdk.Color.parse (color_show_custom_warning); + this.color_warning = Gtk.ColorButton.new_with_color (color); + this.color_warning.connect ('color-set', (o) => { + settings.set_string (COLOR_SHOW_CUSTOM_WARNING_KEY, this.color_string (o.rgba)); + }); + this.colorbox.pack_start (this.color_warning, true, false, 0); + + this.colorbox.add (new Gtk.Label ({label: _("Critical")})); + [ ,color] = Gdk.Color.parse (color_show_custom_critical); + this.color_critical = Gtk.ColorButton.new_with_color (color); + this.color_critical.connect ('color-set', (o) => { + settings.set_string (COLOR_SHOW_CUSTOM_CRITICAL_KEY, this.color_string (o.rgba)); + }); + this.colorbox.pack_start (this.color_critical, true, false, 0); + this.colorbox.sensitive = color_show && color_show_custom; + + this.show_all (); + }, + + color_string: function (rgba) { + let s = "#%02x%02x%02x".format ( + this.scale_round (rgba.red), + this.scale_round (rgba.green), + this.scale_round (rgba.blue) + ); + return s; + }, + + scale_round: function (val) { + val = Math.floor (val * 255 + 0.5); + val = Math.max (val, 0); + val = Math.min (val, 255); + return val; + } +}); + +var PagePowerCPUFreq = new Lang.Class({ + Name: 'PagePowerCPUFreq', + Extends: Gtk.Box, + + _init: function () { + this.parent ({orientation:Gtk.Orientation.VERTICAL, margin:6}); + this.border_width = 6; + + this.unplug = new PowerProfile (eprofiles[EventType.DISCHARGING], + _("On Battery"), + + _("You can set less than 100% of the battery level. ") + + _("It will do not apply \"Powersaving Profile\" immediately on the event. ") + + _("It could be helpful on temporary disconnections, issues with a power connector, etc.") + ); + this.add (this.unplug); + this.unplug.combo.connect ('changed', (o) => { + if (o.active == 0) + eprofiles[EventType.DISCHARGING].guid = ""; + else if (o.active <= auto_profiles.length) + eprofiles[EventType.DISCHARGING].guid = auto_profiles[o.active - 1].guid; + else + eprofiles[EventType.DISCHARGING].guid = profiles[o.active - auto_profiles.length - 1].guid; + //this.unplug.slider.set_value (100); + settings.set_string (EPROFILES_KEY, JSON.stringify (eprofiles)); + }); + this.unplug.slider.connect ('value_changed', (o) => { + eprofiles[EventType.DISCHARGING].percent = Math.round (o.get_value ()); + settings.set_string (EPROFILES_KEY, JSON.stringify (eprofiles)); + }); + + this.show_all (); + } +}); + +var PowerProfile = new Lang.Class({ + Name: 'PowerProfile', + Extends: Gtk.Box, + + _init: function (profile, text, tooltip) { + this.parent ({orientation:Gtk.Orientation.VERTICAL, margin:6}); + this.tooltip_text = tooltip; + this.border_width = 6; + let id = 0, i = 1; + + let hbox = new Gtk.Box ({orientation:Gtk.Orientation.HORIZONTAL}); + this.pack_start (hbox, false, false, 0); + hbox.add (new Gtk.Label ({label:""+text+"",use_markup:true,xalign:0})); + this.combo = new Gtk.ComboBoxText (); + this.combo.tooltip_text = _("Power Profile"); + this.combo.append_text (_("do nothing")); + auto_profiles.forEach (s => { + this.combo.append_text (s.name); + if (s.guid == profile.guid) id = i; + i++; + }); + profiles.forEach (s => { + this.combo.append_text (s.name + " (user profile)"); + if (s.guid == profile.guid) id = i; + i++; + }); + this.combo.active = id; + hbox.pack_end (this.combo, false, false, 0); + + hbox = new Gtk.Box ({orientation:Gtk.Orientation.HORIZONTAL}); + hbox.margin_top = 8; + this.add (hbox); + this.label = new Gtk.Label ({label:_("Level"), use_markup:true, xalign:0}); + hbox.pack_start (this.label, true, true, 0); + this.info = new Gtk.Label ({label:""+profile.percent+"%", use_markup:true}); + hbox.pack_end (this.info, false, false, 0); + this.slider = Gtk.Scale.new_with_range (Gtk.Orientation.HORIZONTAL, 0, 100, 1); + this.slider.draw_value = false; + this.slider.set_value (profile.percent); + this.add (this.slider); + this.slider.connect('value_changed', (o) => { + this.update_info (Math.round (o.get_value ()).toString ()); + }); + + this.show_all (); + }, + + update_info: function (info) { + this.info.set_markup ("" + info + "%"); + } +}); + +const css_theme = " \ +.cpufreq-text { font-family: cpufreq, roboto, cantarell} \ +"; + +function get_css_provider () { + let cssp = new Gtk.CssProvider (); + try { + cssp.load_from_data (css_theme); + } catch (e) { + print (e); + cssp = null; + } + return cssp; +} + +function getCurrentFile () { + let stack = (new Error()).stack; + let stackLine = stack.split('\n')[1]; + if (!stackLine) + throw new Error ('Could not find current file'); + let match = new RegExp ('@(.+):\\d+').exec(stackLine); + if (!match) + throw new Error ('Could not find current file'); + let path = match[1]; + let file = path.startsWith('file://') ? Gio.File.new_for_uri(path) : Gio.File.new_for_path (path); + return [file.get_path(), file.get_parent().get_path(), file.get_basename()]; +} + +function debug (msg) { + if (msg) print ("[cpufreq][prefs] " + msg); +} + +function error (msg) { + log ("[cpufreq][prefs] (EE) " + msg); +} + +function init () { + Convenience.initTranslations (); +} + +function buildPrefsWidget () { + if (Gtk.MAJOR_VERSION > 3) { + GLib.spawn_command_line_async (APPDIR + "/cpufreq-preferences"); + let widget = new DumpWidget (); + return widget; + } + let widget = new CPUFreqPreferences (); + return widget.notebook; +} + +var DumpWidget = new Lang.Class({ + Name: 'DumpWidget', + Extends: Gtk.Box, + + _init: function () { + this.parent ({}); + /*this.connect ('realize', () => { + this.get_parent ().get_parent ().close (); + });*/ + } +}); + +function get_appdir () { + let s = getCurrentFile ()[1]; + if (GLib.file_test (s + "/prefs.js", GLib.FileTest.EXISTS)) return s; + s = GLib.get_home_dir () + "/.local/share/gnome-shell/extensions/cpufreq@konkor"; + if (GLib.file_test (s + "/prefs.js", GLib.FileTest.EXISTS)) return s; + s = "/usr/local/share/gnome-shell/extensions/cpufreq@konkor"; + if (GLib.file_test (s + "/prefs.js", GLib.FileTest.EXISTS)) return s; + s = "/usr/share/gnome-shell/extensions/cpufreq@konkor"; + if (GLib.file_test (s + "/prefs.js", GLib.FileTest.EXISTS)) return s; + throw "CPUFreq installation not found..."; + return s; +}