10 Commits
v10 ... v16

6 changed files with 206 additions and 120 deletions

View File

@@ -150,7 +150,7 @@ You can test it with command line :
gdbus call --session --dest org.gnome.Shell --object-path /com/soutade/GenericMonitor --method com.soutade.GenericMonitor.deleteGroups '{"groups":["new"]}' gdbus call --session --dest org.gnome.Shell --object-path /com/soutade/GenericMonitor --method com.soutade.GenericMonitor.deleteGroups '{"groups":["new"]}'
Python examples is available Python examples are available @ https://indefero.soutade.fr/p/genericmonitor/source/tree/master/examples
Development Development

85
examples/gmail.py Normal file
View File

@@ -0,0 +1,85 @@
#
# Copyright 2018 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Setup : https://developers.google.com/gmail/api/quickstart/python
# Need to have credentials.json were you call main script
#
# File imported from https://github.com/googleworkspace/python-samples/blob/main/gmail/quickstart/quickstart.py
#
from __future__ import print_function
import os.path
import json
from google.auth.transport.requests import Request
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from googleapiclient.discovery import build
# If modifying these scopes, delete the file token.json.
SCOPES = ['https://www.googleapis.com/auth/gmail.readonly']
creds = None
def _initCreds():
global creds
if creds: return
# The file token.json stores the user's access and refresh tokens, and is
# created automatically when the authorization flow completes for the first
# time.
if os.path.exists('token.json'):
creds = Credentials.from_authorized_user_file('./token.json', SCOPES)
# If there are no (valid) credentials available, let the user log in.
if not creds or not creds.valid:
if creds and creds.expired and creds.refresh_token:
creds.refresh(Request())
else:
flow = InstalledAppFlow.from_client_secrets_file(
'credentials.json', SCOPES)
creds = flow.run_local_server(port=0)
# Save the credentials for the next run
with open('token.json', 'w') as token:
data = {}
data['refresh_token'] = creds.refresh_token
data['client_id'] = creds.client_id
data['client_secret'] = creds.client_secret
data['token_uri'] = creds.token_uri
data['id_token'] = creds.id_token
token.write(json.dumps(data))
def getUnreadMails():
"""
Get number of unread threads (that may contain multiple messages)
"""
_initCreds()
service = build('gmail', 'v1', credentials=creds)
pageToken = ''
threads = set()
while True:
results = service.users().messages().list(userId='me', labelIds=['UNREAD'],\
includeSpamTrash=False, pageToken=pageToken)\
.execute()
if not 'messages' in results.keys(): continue
threads = threads.union(set([k['threadId'] for k in results['messages']]))
# Loop over all result pages (100 results per page by default)
pageToken = results.get('nextPageToken', '')
if not pageToken: break
return len(threads)

View File

@@ -23,11 +23,11 @@ import time
import requests import requests
from requests.auth import HTTPBasicAuth from requests.auth import HTTPBasicAuth
import xml.dom.minidom import xml.dom.minidom
import getpass
from threading import Thread from threading import Thread
from signal import signal, SIGINT from signal import signal, SIGINT
import sys import sys
from genericmonitor import * from genericmonitor import *
from gmail import getUnreadMails
PURPLE_CONV_UPDATE_UNSEEN = 4 PURPLE_CONV_UPDATE_UNSEEN = 4
PURPLE_MESSAGE_SEND = 0 PURPLE_MESSAGE_SEND = 0
@@ -59,31 +59,23 @@ class PidginConversation:
class EventThread(Thread,GenericMonitor): class EventThread(Thread,GenericMonitor):
SLEEP_TIME = 30 SLEEP_TIME = 30
MAIL_ADDRESS='XXX@gmail.com'
def stop(self): def stop(self):
self._stopLoop = True self._stopLoop = True
self.stopMainLoop() self.stopMainLoop()
def _getMail(self): def _getMail(self):
address = "https://mail.google.com/mail/feed/atom"
auth = HTTPBasicAuth(self.MAIL_ADDRESS, self._mail_password)
req = requests.get(address, auth=auth)
text = '' text = ''
style = '' style = ''
if req.status_code == requests.codes.ok: try:
dom = xml.dom.minidom.parseString(req.text) nb_messages = getUnreadMails()
try: if nb_messages == 1:
nb_messages = int(dom.getElementsByTagName('fullcount')[0].firstChild.nodeValue) text = '1 msg'
if nb_messages == 1: elif nb_messages > 1:
text = '1 msg' text = '%d msgs' % (nb_messages)
elif nb_messages > 1: style = 'color:white'
text = '%d msgs' % (nb_messages) except Exception as e:
style = 'color:white' text = str(e)
except Exception as e:
text = str(e)
else:
text = 'Mail error %d' % (req.status_code)
self.mailWidget.setText(text) self.mailWidget.setText(text)
self.mailWidget.setStyle(style) self.mailWidget.setStyle(style)
@@ -103,8 +95,6 @@ class EventThread(Thread,GenericMonitor):
self.add_signal_receiver(self.pidginMessageWrote, 'WroteChatMsg', 'im.pidgin.purple.PurpleInterface') self.add_signal_receiver(self.pidginMessageWrote, 'WroteChatMsg', 'im.pidgin.purple.PurpleInterface')
self.add_signal_receiver(self.pidginConversationUpdated, 'ConversationUpdated', 'im.pidgin.purple.PurpleInterface') self.add_signal_receiver(self.pidginConversationUpdated, 'ConversationUpdated', 'im.pidgin.purple.PurpleInterface')
self._mail_password = getpass.getpass('Enter password for address %s: ' % (self.MAIL_ADDRESS))
self.mailWidget = GenericMonitorTextWidget('') self.mailWidget = GenericMonitorTextWidget('')
mailItem = GenericMonitorItem('mail', [self.mailWidget]) mailItem = GenericMonitorItem('mail', [self.mailWidget])
self.mailGroup = GenericMonitorGroup('Mail', [mailItem]) self.mailGroup = GenericMonitorGroup('Mail', [mailItem])

View File

@@ -43,20 +43,17 @@ class PicturePopup(GenericMonitor):
self.runMainLoop() self.runMainLoop()
def display_next_img(self): def display_next_img(self):
filedata = urllib.request.urlopen('https://source.unsplash.com/random') filedata = urllib.request.urlopen('https://picsum.photos/500/500')
# Get redirected URL without parameters
url = filedata.url.split('?')[0]
filedata = urllib.request.urlopen(url + '?fit=max&width=500&height=500')
datatowrite = filedata.read() datatowrite = filedata.read()
with open('/tmp/cat2.jpg', 'wb') as f: with open('/tmp/cat2.jpg', 'wb') as f:
f.write(datatowrite) f.write(datatowrite)
widget = GenericMonitorTextWidget('#%d' % self.imgs_idx, 'color:purple') widget = GenericMonitorTextWidget('#%d' % self.imgs_idx, 'color:purple')
url_widget = GenericMonitorTextWidget(url, 'color:white;font-weight:bold', signals={'on-click':'signal'}) # No name here url_widget = GenericMonitorTextWidget('random_pic', 'color:white;font-weight:bold', signals={'on-click':'signal'}) # No name here
picture_widget = GenericMonitorPictureWidget('/tmp/cat2.jpg', name='NestedWidget', signals={'on-click':'signal'}) picture_widget = GenericMonitorPictureWidget('/tmp/cat2.jpg', name='NestedWidget', signals={'on-click':'signal'})
popup = GenericMonitorPopup([url_widget, picture_widget]) popup = GenericMonitorPopup([url_widget, picture_widget])
signals = { signals = {
'on-click':'toggle-popup', 'on-click':'toggle-popup',
# Could also use this behavior # Could also use this behavior [bugged since GNOME 42]
# 'on-enter':'open-popup', # 'on-enter':'open-popup',
# 'on-leave':'close-popup', # 'on-leave':'close-popup',
'on-dblclick':'signal', 'on-dblclick':'signal',

View File

@@ -24,22 +24,22 @@
https://github.com/bananenfisch/RecentItems/blob/master/extension.js https://github.com/bananenfisch/RecentItems/blob/master/extension.js
https://github.com/julio641742/gnome-shell-extension-reference/blob/master/tutorials/POPUPMENU-EXTENSION.md https://github.com/julio641742/gnome-shell-extension-reference/blob/master/tutorials/POPUPMENU-EXTENSION.md
https://gjs-docs.gnome.org/st10~1.0_api/st.widget https://gjs-docs.gnome.org/st10~1.0_api/st.widget
https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/master/js/ui/panelMenu.js https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/panelMenu.js
https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/master/js/ui/popupMenu.js https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/popupMenu.js
https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/master/js/ui/panel.js https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/panel.js
*/ */
const St = imports.gi.St; import * as Extension from 'resource:///org/gnome/shell/extensions/extension.js';
const Gio = imports.gi.Gio; import St from 'gi://St';
const GLib = imports.gi.GLib import Gio from 'gi://Gio';
const Main = imports.ui.main; import GLib from 'gi://GLib';
const Mainloop = imports.mainloop; import Clutter from 'gi://Clutter';
const Clutter = imports.gi.Clutter; import GObject from 'gi://GObject';
const PanelMenu = imports.ui.panelMenu; import Pixbuf from 'gi://GdkPixbuf';
const PopupMenu = imports.ui.popupMenu; import Cogl from 'gi://Cogl';
const GObject = imports.gi.GObject; import * as Main from 'resource:///org/gnome/shell/ui/main.js';
const Pixbuf = imports.gi.GdkPixbuf; import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
const Cogl = imports.gi.Cogl; import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
function hashGet(hash, key, defaultValue) { function hashGet(hash, key, defaultValue) {
@@ -49,23 +49,20 @@ function hashGet(hash, key, defaultValue) {
return defaultValue; return defaultValue;
} }
function log(message) {
global.log('[GenericMontior]', message);
}
class SignalMgt { class SignalMgt {
constructor(item, name, group, dbus, menu) { constructor(item, name, group, dbus, logger, buttonMenu) {
this.name = name; this.name = name;
this.group = group; this.group = group;
this.fullname = this.name + '@' + this.group; this.fullname = this.name + '@' + this.group;
this.dbus = dbus; this.dbus = dbus;
this.menu = menu; this.logger = logger;
this.buttonMenu = buttonMenu;
this.signals = new WeakMap(); this.signals = new WeakMap();
this.widgets = new Array(); this.widgets = new Array();
this.timeouts = new Array(); this.timeouts = new Array();
this.menuOpen = false;
this.nbClicks = 0; this.nbClicks = 0;
this.button = -1; this.button = -1;
@@ -78,7 +75,7 @@ class SignalMgt {
this.onScroll = hashGet(item, 'on-scroll', ''); this.onScroll = hashGet(item, 'on-scroll', '');
} }
destructor() { destroy() {
for(let widgetIdx in this.widgets) for(let widgetIdx in this.widgets)
this.disconnectWidgetSignals(this.widgets[widgetIdx]); this.disconnectWidgetSignals(this.widgets[widgetIdx]);
for(let timeoutIdx in this.timeouts) for(let timeoutIdx in this.timeouts)
@@ -99,6 +96,7 @@ class SignalMgt {
this.widgets.push(widget); this.widgets.push(widget);
let array = new Array(); let array = new Array();
this.signals.set(widget, array); this.signals.set(widget, array);
widget.set_reactive(true);
let id; let id;
id = widget.connect('enter-event', this._onEnter.bind(this)); id = widget.connect('enter-event', this._onEnter.bind(this));
array.push(id); array.push(id);
@@ -106,7 +104,6 @@ class SignalMgt {
array.push(id); array.push(id);
id = widget.connect('scroll-event', this._onScroll.bind(this)); id = widget.connect('scroll-event', this._onScroll.bind(this));
array.push(id); array.push(id);
widget.set_reactive(true);
id = widget.connect('button-release-event', this._clicked.bind(this)); id = widget.connect('button-release-event', this._clicked.bind(this));
array.push(id); array.push(id);
} }
@@ -119,24 +116,37 @@ class SignalMgt {
} }
_manageEventAction(action, signalName) { _manageEventAction(action, signalName) {
/*
GNOME 48 : onEnter && this.buttonMenu.menuOpen == close popup without onClick event
*/
if (signalName == "onEnter" && this.buttonMenu.menuOpen)
{
this.buttonMenu.menuOpen = false;
return Clutter.EVENT_STOP;
}
if (action === 'open-popup') if (action === 'open-popup')
this.menu.open(true); {
this.buttonMenu.openPopup();
}
else if (action === 'close-popup') else if (action === 'close-popup')
this.menu.close(); {
this.buttonMenu.closePopup();
}
else if (action === 'toggle-popup') else if (action === 'toggle-popup')
this.menu.toggle(); {
else if (action == 'delete') this.buttonMenu.togglePopup();
}
else if (action === 'delete')
this.dbus.deleteItem(this, this.group); this.dbus.deleteItem(this, this.group);
else if (action === 'signal') else if (action === 'signal')
this.dbus.emitSignal(signalName, this.fullname); this.dbus.emitSignal(signalName, this.fullname);
else
return Clutter.EVENT_PROPAGATE;
return Clutter.EVENT_PROPAGATE; return Clutter.EVENT_STOP;
} }
_manageLeaveEvent() {
this._manageEventAction(this.onLeave);
}
_doClickCallback() { _doClickCallback() {
let right = ''; let right = '';
let nbClicks = ''; let nbClicks = '';
@@ -147,7 +157,7 @@ class SignalMgt {
nbClicks = 'Dbl'; nbClicks = 'Dbl';
const signalName = 'on' + right + nbClicks + 'Click'; const signalName = 'on' + right + nbClicks + 'Click';
let action = 'signal'; let action = 'signal';
switch(signalName) { switch(signalName) {
case 'onClick': action = this.onClick; break; case 'onClick': action = this.onClick; break;
@@ -156,11 +166,11 @@ class SignalMgt {
case 'onRightDblClick': action = this.onRightDblClick; break; case 'onRightDblClick': action = this.onRightDblClick; break;
} }
this._manageEventAction(action, signalName);
this.nbClicks = 0; this.nbClicks = 0;
this.button = -1; this.button = -1;
this._manageEventAction(action, signalName);
return false; return false;
} }
@@ -171,12 +181,13 @@ class SignalMgt {
this.button = event.get_button(); this.button = event.get_button();
this.nbClicks = 1; this.nbClicks = 1;
let sourceId = Mainloop.timeout_add(this.dbus.ClutterSettings['double-click-time'], let sourceId = GLib.timeout_add(GLib.G_PRIORITY_DEFAULT,
this._doClickCallback.bind(this)); this.dbus.ClutterSettings['double-click-time'],
this._doClickCallback.bind(this));
this.timeouts.push(sourceId); this.timeouts.push(sourceId);
} }
return Clutter.EVENT_PROPAGATE; return Clutter.EVENT_STOP;
} }
_onEnter(/*actor, event*/) { _onEnter(/*actor, event*/) {
@@ -207,10 +218,10 @@ class MyPopupMenuItem extends PopupMenu.PopupBaseMenuItem {
super._init(params); super._init(params);
this.box = new St.BoxLayout({ style_class: 'popup-combobox-item' }); this.box = new St.BoxLayout({ style_class: 'popup-combobox-item' });
this.box.set_vertical(true); this.box.set_orientation(Clutter.Orientation.VERTICAL);
for (let widgetIndex in widgets) for (let widgetIndex in widgets)
this.box.add(widgets[widgetIndex]); this.box.add_child(widgets[widgetIndex]);
this.add_child(this.box); this.add_child(this.box);
} }
@@ -222,14 +233,16 @@ var MonitorWidget = GObject.registerClass({
}, },
class MonitorWidget extends PanelMenu.Button { class MonitorWidget extends PanelMenu.Button {
_init(item, group, dbus, position) { _init(item, group, dbus, logger, position) {
super._init(0.0); super._init(0.0);
this.name = item['name']; this.name = item['name'];
this.group = group; this.group = group;
this.fullname = this.name + '@' + this.group; this.fullname = this.name + '@' + this.group;
this.dbus = dbus; this.dbus = dbus;
this.signalManager = new SignalMgt(item, this.name, group, dbus, this.menu); this.logger = logger;
this.menuItem = null;
this.signalManager = new SignalMgt(item, this.name, group, dbus, logger, this);
this.popup_signals = null; this.popup_signals = null;
this.popup_widgets = null; this.popup_widgets = null;
@@ -362,18 +375,24 @@ class MonitorWidget extends PanelMenu.Button {
openPopup() { openPopup() {
this.menu.open(true); this.menu.open(true);
this.menuOpen = this.menu.isOpen = true;
} }
closePopup() { closePopup() {
this.menu.close(); this.menu.close();
this.menuOpen = this.menu.isOpen = false;
} }
togglePopup() { togglePopup() {
this.menu.toggle(); if (this.menuOpen)
this.closePopup();
else
this.openPopup();
} }
destroy() { destroy() {
this.menu.close(); this.menu.close();
this.signalManager.destroy();
super.destroy(); super.destroy();
} }
@@ -403,7 +422,7 @@ class MonitorWidget extends PanelMenu.Button {
nestedItem = widgetDict['picture']; nestedItem = widgetDict['picture'];
widget = this._createPicture(nestedItem); widget = this._createPicture(nestedItem);
} else { } else {
log('No known widget defined in popup'); this.logger.error('No known widget defined in popup');
} }
if (nestedItem === null) { if (nestedItem === null) {
@@ -414,7 +433,7 @@ class MonitorWidget extends PanelMenu.Button {
const name = hashGet(nestedItem, 'name', ''); const name = hashGet(nestedItem, 'name', '');
this.popup_signals[widget] = new SignalMgt(nestedItem, name, this.popup_signals[widget] = new SignalMgt(nestedItem, name,
this.fullname, this.dbus, this.fullname, this.dbus,
this.menu); this.logger, this);
this.popup_signals[widget].connectWidgetSignals(widget); this.popup_signals[widget].connectWidgetSignals(widget);
this.popup_widgets.push(widget); this.popup_widgets.push(widget);
} }
@@ -439,7 +458,7 @@ class MonitorWidget extends PanelMenu.Button {
__createText(item, isLabel) { __createText(item, isLabel) {
if (!item.hasOwnProperty('text')) { if (!item.hasOwnProperty('text')) {
log('Text must have a \'text\' value'); this.logger.error('Text must have a \'text\' value');
return null; return null;
} }
@@ -479,7 +498,7 @@ class MonitorWidget extends PanelMenu.Button {
_createIcon(item) { _createIcon(item) {
if (!item.hasOwnProperty('path')) { if (!item.hasOwnProperty('path')) {
log('Icon must have a \'path\' value'); this.logger.error('Icon must have a \'path\' value');
return null; return null;
} }
@@ -500,7 +519,7 @@ class MonitorWidget extends PanelMenu.Button {
_createPicture(item) { _createPicture(item) {
if (!item.hasOwnProperty('path')) { if (!item.hasOwnProperty('path')) {
log('Picture must have a \'path\' value'); this.logger.error('Picture must have a \'path\' value');
return null; return null;
} }
@@ -513,9 +532,11 @@ class MonitorWidget extends PanelMenu.Button {
if (typeof(height) === 'string') if (typeof(height) === 'string')
height = parseInt(height, 10); height = parseInt(height, 10);
const img = new Clutter.Image();
const initialPixbuf = Pixbuf.Pixbuf.new_from_file(item['path']); const initialPixbuf = Pixbuf.Pixbuf.new_from_file(item['path']);
img.set_data(initialPixbuf.get_pixels(), const img = St.ImageContent.new_with_preferred_size(initialPixbuf.get_width(),
initialPixbuf.get_height());
img.set_data(global.stage.context.get_backend().get_cogl_context(),
initialPixbuf.get_pixels(),
initialPixbuf.get_has_alpha() ? Cogl.PixelFormat.RGBA_8888 initialPixbuf.get_has_alpha() ? Cogl.PixelFormat.RGBA_8888
: Cogl.PixelFormat.RGB_888, : Cogl.PixelFormat.RGB_888,
initialPixbuf.get_width(), initialPixbuf.get_width(),
@@ -536,14 +557,12 @@ class MonitorWidget extends PanelMenu.Button {
// From https://github.com/ubuntu/gnome-shell-extension-appindicator/blob/master/interfaces.js // From https://github.com/ubuntu/gnome-shell-extension-appindicator/blob/master/interfaces.js
// loads a xml file into an in-memory string // loads a xml file into an in-memory string
function loadInterfaceXml(filename) { function loadInterfaceXml(extension, filename) {
const extension = imports.misc.extensionUtils.getCurrentExtension();
const interfacesDir = extension.dir.get_child('.'); const interfacesDir = extension.dir.get_child('.');
const file = interfacesDir.get_child(filename); const file = interfacesDir.get_child(filename);
let [result, contents] = imports.gi.GLib.file_get_contents(file.get_path()); let [result, contents] = GLib.file_get_contents(file.get_path());
if (result) { if (result) {
// HACK: The "" + trick is important as hell because file_get_contents returns // HACK: The "" + trick is important as hell because file_get_contents returns
@@ -552,7 +571,10 @@ function loadInterfaceXml(filename) {
// is no `XML` on very recent SpiderMonkey releases (or, if SpiderMonkey is old enough, // is no `XML` on very recent SpiderMonkey releases (or, if SpiderMonkey is old enough,
// will spit out a TypeError soon). // will spit out a TypeError soon).
if (contents instanceof Uint8Array) if (contents instanceof Uint8Array)
contents = imports.byteArray.toString(contents); {
const decoder = new TextDecoder();
contents = decoder.decode(contents);
}
const res = `<node>${contents}</node>`; const res = `<node>${contents}</node>`;
return res; return res;
} else { } else {
@@ -561,11 +583,12 @@ function loadInterfaceXml(filename) {
} }
class GenericMonitorDBUS { class GenericMonitorDBUS {
constructor() { constructor(extension) {
this.logger = extension.getLogger();
this.monitor_groups = {}; this.monitor_groups = {};
this.actor_clicked = {}; this.actor_clicked = {};
this.ClutterSettings = Clutter.Settings.get_default(); this.ClutterSettings = Clutter.Settings.get_default();
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(loadInterfaceXml('dbus.xml'), this); this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(loadInterfaceXml(extension, 'dbus.xml'), this);
this._dbusImpl.export(Gio.DBus.session, '/com/soutade/GenericMonitor'); this._dbusImpl.export(Gio.DBus.session, '/com/soutade/GenericMonitor');
this._dbusImpl.emit_signal('onActivate', null); this._dbusImpl.emit_signal('onActivate', null);
} }
@@ -576,19 +599,19 @@ class GenericMonitorDBUS {
_checkParameters(parameters) { _checkParameters(parameters) {
if (!parameters.hasOwnProperty('group')) { if (!parameters.hasOwnProperty('group')) {
log('No group defined'); this.logger.error('No group defined');
return false; return false;
} }
if (!parameters.hasOwnProperty('items')) { if (!parameters.hasOwnProperty('items')) {
log('No items defined'); this.logger.error('No items defined');
return false; return false;
} }
for (let itemIndex in parameters['items']) { for (let itemIndex in parameters['items']) {
const item = parameters['items'][itemIndex]; const item = parameters['items'][itemIndex];
if (!item.hasOwnProperty('name')) { if (!item.hasOwnProperty('name')) {
log('No name defined for item'); this.logger.error('No name defined for item');
return false; return false;
} }
} }
@@ -609,7 +632,7 @@ class GenericMonitorDBUS {
_getItemFromFullName(fullname) { _getItemFromFullName(fullname) {
const splitName = fullname.split('@'); const splitName = fullname.split('@');
if (splitName.length !== 2) { if (splitName.length !== 2) {
log(`Invalid name ${fullname}`); this.logger.error(`Invalid name ${fullname}`);
return null; return null;
} }
if (!this.monitor_groups.hasOwnProperty(splitName[1])) if (!this.monitor_groups.hasOwnProperty(splitName[1]))
@@ -664,7 +687,7 @@ class GenericMonitorDBUS {
if (position >= childrens.length) if (position >= childrens.length)
position = -1; position = -1;
} }
monitorWidget = new MonitorWidget(item, groupName, this, position); monitorWidget = new MonitorWidget(item, groupName, this, this.logger, position);
group.push(monitorWidget); group.push(monitorWidget);
} else { } else {
monitorWidget.update(item); monitorWidget.update(item);
@@ -686,7 +709,7 @@ class GenericMonitorDBUS {
const parameters = JSON.parse(str); const parameters = JSON.parse(str);
if (!parameters.hasOwnProperty('items')) { if (!parameters.hasOwnProperty('items')) {
log('No items defined'); this.logger.error('No items defined');
return false; return false;
} }
@@ -694,7 +717,7 @@ class GenericMonitorDBUS {
let itemName = parameters['items'][itemIndex]; let itemName = parameters['items'][itemIndex];
const splitName = itemName.split('@'); const splitName = itemName.split('@');
if (splitName.length !== 2) { if (splitName.length !== 2) {
log(`Invalid name ${itemName}`); this.logger.error(`Invalid name ${itemName}`);
return false; return false;
} }
itemName = splitName[0]; itemName = splitName[0];
@@ -713,7 +736,7 @@ class GenericMonitorDBUS {
const parameters = JSON.parse(str); const parameters = JSON.parse(str);
if (!parameters.hasOwnProperty('groups')) { if (!parameters.hasOwnProperty('groups')) {
log('No groups defined'); this.logger.error('No groups defined');
return false; return false;
} }
@@ -737,7 +760,7 @@ class GenericMonitorDBUS {
const parameters = JSON.parse(str); const parameters = JSON.parse(str);
if (!parameters.hasOwnProperty('item')) { if (!parameters.hasOwnProperty('item')) {
log('No item defined'); this.logger.error('No item defined');
return false; return false;
} }
@@ -745,7 +768,7 @@ class GenericMonitorDBUS {
if (monitorWidget !== null) if (monitorWidget !== null)
return monitorWidget; return monitorWidget;
else else
log(`Item ${str} not found`); this.logger.error(`Item ${str} not found`);
return null; return null;
} }
@@ -768,7 +791,7 @@ class GenericMonitorDBUS {
monitorWidget.togglePopup(); monitorWidget.togglePopup();
} }
destructor() { destroy() {
this._dbusImpl.emit_signal('onDeactivate', null); this._dbusImpl.emit_signal('onDeactivate', null);
for (let groupIndex in this.monitor_groups) { for (let groupIndex in this.monitor_groups) {
const group = this.monitor_groups[groupIndex]; const group = this.monitor_groups[groupIndex];
@@ -780,26 +803,21 @@ class GenericMonitorDBUS {
} }
} }
class Extension { export default class GenericMonitorExtension extends Extension.Extension {
constructor(...args) {
super(...args);
this.textDBusService = null;
}
enable() { enable() {
this.textDBusService = new GenericMonitorDBUS(); this.textDBusService = new GenericMonitorDBUS(this);
} }
disable() { disable() {
this.textDBusService.destructor(); if (this.textDBusService !== null) {
delete this.textDBusService; this.textDBusService.destroy();
delete this.textDBusService;
this.textDBusService = null;
}
} }
} }
const extension = new Extension();
function init() {
}
function enable() {
extension.enable();
}
function disable() {
extension.disable();
}

View File

@@ -2,13 +2,9 @@
"uuid": "generic-monitor@gnome-shell-extensions", "uuid": "generic-monitor@gnome-shell-extensions",
"name": "Generic Monitor", "name": "Generic Monitor",
"description": "Display text & icon on systray using DBUS", "description": "Display text & icon on systray using DBUS",
"version": "10", "version": "16",
"shell-version": [ "shell-version": [
"42", "48"
"41",
"40",
"3.38",
"3.36"
], ],
"url": "http://indefero.soutade.fr/p/genericmonitor" "url": "https://forge.soutade.fr/soutade/GnomeShellGenericMonitor"
} }