18 Commits
v4 ... v15

Author SHA1 Message Date
ca11b0e175 Version 15 2024-08-31 16:36:07 +02:00
d0e8f0cde5 Code review from JustPerfection 2024-08-31 13:58:43 +02:00
66d79cc804 Update for Gnome Shell 45+ 2024-08-29 20:46:21 +02:00
3ed3394a33 Update metadata.json with Gnome Shell 44 2023-09-25 19:14:35 +02:00
7cd668af14 Count number of threads instead of unread messages in gmail example 2022-11-20 21:01:41 +01:00
5690307c63 Update Gmail access with OAuth 2 2022-11-20 20:00:34 +01:00
b1feb004e8 Update metadata.json 2022-10-28 17:20:14 +02:00
73021a25a1 Update README 2022-10-28 08:09:34 +02:00
4c73986596 Updates for GNOME 43 2022-10-27 17:54:37 +02:00
d3665f2ff0 Quick update for GNOME 42. There is still some issues with menu. 2022-04-10 14:21:53 +02:00
00fb6120c9 Update README.md and version to be aligned on Gnome store version 2022-01-07 08:35:13 +01:00
02d3e8b72a Remove timer resource during SignalMgt destructor 2022-01-06 09:52:11 +01:00
78254a550e Update support for version 41 in metadata 2022-01-06 09:14:55 +01:00
7a5f506c23 Remove use of deprecated "lang" package 2021-10-01 09:02:34 +02:00
1d3328f71e Update support for version 40 in metadata 2021-09-30 16:33:06 +02:00
45d0cc9d11 Use WeakMap to store objects as key and rename MonitorWidget signals field into signalManager to avoid confusions 2021-09-30 16:32:26 +02:00
4d6fe82790 Update version 2021-02-02 07:04:45 +01:00
9943a9075f Popup text must be labels not buttons 2021-01-31 20:18:11 +01:00
7 changed files with 249 additions and 133 deletions

View File

@@ -7,6 +7,10 @@ This GNOME Shell Extension aims to display information to center box. Using DBUS
Installation
------------
Install it from https://extensions.gnome.org/extension/2826/generic-monitor/
OR
Create a symbolic link from your _.local_ directory and enable extension
ln -s $PWD/generic-monitor@gnome-shell-extensions/ ~/.local/share/gnome-shell/extensions/
@@ -109,11 +113,11 @@ Signals can be :
Targets :
* signal : emit a signal to desktop application
* delete : Delete item
* open-popup : Open the popup if there is one
* close-popup : Close the popup if there is one
* toggle-popup : Toggle (open/close) the popup if there is one
* signal : Emit a signal to desktop application
* delete : Delete item
* open-popup : Open the popup if there is one
* close-popup : Close the popup if there is one
* toggle-popup : Toggle (open/close) the popup if there is one
Signal names emit when action "signal" is specified :
@@ -142,11 +146,11 @@ Example
You can test it with command line :
gdbus call --session --dest org.gnome.Shell --object-path /com/soutade/GenericMonitor --method com.soutade.GenericMonitor.notify '{"group":"new","items":[{"name":"first","on-click":"toggle-popup","text":{"text":"Hello","style":"color:green"},"popup":{"items":[{"picture":{"path":"/tmp/cat2.jpg"}}]}}]}'
gdbus call --session --dest org.gnome.Shell --object-path /com/soutade/GenericMonitor --method com.soutade.GenericMonitor.notify '{"group":"new","items":[{"name":"first","on-click":"toggle-popup","text":{"text":"Hello","style":"color:green"},"popup":{"items":[{"picture":{"path":"/tmp/cat.jpg"}}]}}]}'
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

View File

@@ -1,47 +1,47 @@
<interface name="com.soutade.GenericMonitor">
<!-- Functions -->
<method name="notify">
<arg type="s" direction="in" />
<arg name="parameters" type="s" direction="in" />
</method>
<method name="deleteItems">
<arg type="s" direction="in" />
<arg name="items" type="s" direction="in" />
</method>
<method name="deleteGroups">
<arg type="s" direction="in" />
<arg name="groups" type="s" direction="in" />
</method>
<method name="openPopup">
<arg type="s" direction="in" />
<arg name="popup" type="s" direction="in" />
</method>
<method name="closePopup">
<arg type="s" direction="in" />
<arg name="popup" type="s" direction="in" />
</method>
<method name="togglePopup">
<arg type="s" direction="in" />
<arg name="popup" type="s" direction="in" />
</method>
<!-- Events -->
<signal name="onClick">
<arg type="s" direction="out" />
<arg name="fullName" type="s" direction="out" />
</signal>
<signal name="onRightClick">
<arg type="s" direction="out" />
<arg name="fullName" type="s" direction="out" />
</signal>
<signal name="onDblClick">
<arg type="s" direction="out" />
<arg name="fullName" type="s" direction="out" />
</signal>
<signal name="onRightDblClick">
<arg type="s" direction="out" />
<arg name="fullName" type="s" direction="out" />
</signal>
<signal name="onScrollUp">
<arg type="s" direction="out" />
<arg name="fullName" type="s" direction="out" />
</signal>
<signal name="onScrollDown">
<arg type="s" direction="out" />
<arg name="fullName" type="s" direction="out" />
</signal>
<signal name="onEnter">
<arg type="s" direction="out" />
<arg name="fullName" type="s" direction="out" />
</signal>
<signal name="onLeave">
<arg type="s" direction="out" />
<arg name="fullName" type="s" direction="out" />
</signal>
<!-- Activate/Deactivate signals -->
<signal name="onActivate">

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

View File

@@ -43,20 +43,17 @@ class PicturePopup(GenericMonitor):
self.runMainLoop()
def display_next_img(self):
filedata = urllib.request.urlopen('https://source.unsplash.com/random')
# Get redirected URL without parameters
url = filedata.url.split('?')[0]
filedata = urllib.request.urlopen(url + '?fit=max&width=500&height=500')
filedata = urllib.request.urlopen('https://picsum.photos/500/500')
datatowrite = filedata.read()
with open('/tmp/cat2.jpg', 'wb') as f:
f.write(datatowrite)
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'})
popup = GenericMonitorPopup([url_widget, picture_widget])
signals = {
'on-click':'toggle-popup',
# Could also use this behavior
# Could also use this behavior [bugged since GNOME 42]
# 'on-enter':'open-popup',
# 'on-leave':'close-popup',
'on-dblclick':'signal',

View File

@@ -24,23 +24,22 @@
https://github.com/bananenfisch/RecentItems/blob/master/extension.js
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://gitlab.gnome.org/GNOME/gnome-shell/-/blob/master/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/master/js/ui/panel.js
https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/panelMenu.js
https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/popupMenu.js
https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/panel.js
*/
const St = imports.gi.St;
const Gio = imports.gi.Gio;
const Lang = imports.lang;
const GLib = imports.gi.GLib
const Main = imports.ui.main;
const Mainloop = imports.mainloop;
const Clutter = imports.gi.Clutter;
const PanelMenu = imports.ui.panelMenu;
const PopupMenu = imports.ui.popupMenu;
const GObject = imports.gi.GObject;
const Pixbuf = imports.gi.GdkPixbuf;
const Cogl = imports.gi.Cogl;
import * as Extension from 'resource:///org/gnome/shell/extensions/extension.js';
import St from 'gi://St';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
import Clutter from 'gi://Clutter';
import GObject from 'gi://GObject';
import Pixbuf from 'gi://GdkPixbuf';
import Cogl from 'gi://Cogl';
import * as Main from 'resource:///org/gnome/shell/ui/main.js';
import * as PanelMenu from 'resource:///org/gnome/shell/ui/panelMenu.js';
import * as PopupMenu from 'resource:///org/gnome/shell/ui/popupMenu.js';
function hashGet(hash, key, defaultValue) {
@@ -51,20 +50,23 @@ function hashGet(hash, key, defaultValue) {
}
function log(message) {
global.log('[GenericMontior]', message);
console.error('[GenericMontior]', message);
}
class SignalMgt {
constructor(item, name, group, dbus, menu) {
constructor(item, name, group, dbus, buttonMenu) {
this.name = name;
this.group = group;
this.fullname = this.name + '@' + this.group;
this.dbus = dbus;
this.menu = menu;
this.signals = {}
this.buttonMenu = buttonMenu;
this.signals = new WeakMap();
this.widgets = new Array();
this.timeouts = new Array();
this.menuOpen = false;
this.nbClicks = 0;
this.button = -1;
@@ -77,9 +79,11 @@ class SignalMgt {
this.onScroll = hashGet(item, 'on-scroll', '');
}
destructor() {
for(let widgetIdx in this.signals)
this.disconnectWidgetSignals(this.signals[widgetIdx]);
destroy() {
for(let widgetIdx in this.widgets)
this.disconnectWidgetSignals(this.widgets[widgetIdx]);
for(let timeoutIdx in this.timeouts)
GLib.Source.remove(this.timeouts[timeoutIdx]);
}
updateSignals(item) {
@@ -93,44 +97,64 @@ class SignalMgt {
}
connectWidgetSignals(widget) {
this.signals[widget] = [];
this.widgets.push(widget);
let array = new Array();
this.signals.set(widget, array);
let id;
id = widget.connect('enter-event', this._onEnter.bind(this));
this.signals[widget].push(id);
array.push(id);
id = widget.connect('leave-event', this._onLeave.bind(this));
this.signals[widget].push(id);
array.push(id);
id = widget.connect('scroll-event', this._onScroll.bind(this));
this.signals[widget].push(id);
array.push(id);
widget.set_reactive(true);
id = widget.connect('button-release-event', Lang.bind(this, this._clicked));
this.signals[widget].push(id);
id = widget.connect('button-release-event', this._clicked.bind(this));
array.push(id);
}
disconnectWidgetSignals(widget) {
for(let idx in this.signals[widget])
widget.disconnect(this.signals[widget][idx]);
this.signals[widget] = null;
let array = this.signals.get(widget);
for(let idx in array)
widget.disconnect(array[idx]);
this.signals.set(widget, null);
}
toggleMenu() {
if (this.menuOpen)
{
this.buttonMenu.menu.close();
this.menuOpen = false;
}
else
{
this.buttonMenu.menu.open(true);
this.menuOpen = true;
}
}
_manageEventAction(action, signalName) {
if (action === 'open-popup')
this.menu.open(true);
{
this.buttonMenu.menu.open(true);
this.menuOpen = true;
}
else if (action === 'close-popup')
this.menu.close();
{
this.buttonMenu.menu.close();
this.menuOpen = false;
}
else if (action === 'toggle-popup')
this.menu.toggle();
else if (action == 'delete')
{
this.toggleMenu();
}
else if (action === 'delete')
this.dbus.deleteItem(this, this.group);
else if (action === 'signal')
this.dbus.emitSignal(signalName, this.fullname);
return Clutter.EVENT_PROPAGATE;
return Clutter.EVENT_STOP;
}
_manageLeaveEvent() {
this._manageEventAction(this.onLeave);
}
_doClickCallback() {
let right = '';
let nbClicks = '';
@@ -165,8 +189,10 @@ class SignalMgt {
this.button = event.get_button();
this.nbClicks = 1;
Mainloop.timeout_add(this.dbus.ClutterSettings['double-click-time'],
Lang.bind(this, this._doClickCallback));
let sourceId = GLib.timeout_add(GLib.G_PRIORITY_DEFAULT,
this.dbus.ClutterSettings['double-click-time'],
this._doClickCallback.bind(this));
this.timeouts.push(sourceId);
}
return Clutter.EVENT_PROPAGATE;
@@ -203,7 +229,7 @@ class MyPopupMenuItem extends PopupMenu.PopupBaseMenuItem {
this.box.set_vertical(true);
for (let widgetIndex in widgets)
this.box.add(widgets[widgetIndex]);
this.box.add_child(widgets[widgetIndex]);
this.add_child(this.box);
}
@@ -222,8 +248,10 @@ class MonitorWidget extends PanelMenu.Button {
this.group = group;
this.fullname = this.name + '@' + this.group;
this.dbus = dbus;
this.signals = new SignalMgt(item, this.name, group, dbus, this.menu);
this.popup_signals = {};
this.menuItem = null;
this.signalManager = new SignalMgt(item, this.name, group, dbus, this);
this.popup_signals = null;
this.popup_widgets = null;
if (item.hasOwnProperty('icon'))
{
@@ -232,7 +260,7 @@ class MonitorWidget extends PanelMenu.Button {
else
this.icon = this._createIcon(item['icon']);
if (this.icon !== null) {
this.signals.connectWidgetSignals(this.icon);
this.signalManager.connectWidgetSignals(this.icon);
this.add_child(this.icon);
}
} else
@@ -246,7 +274,7 @@ class MonitorWidget extends PanelMenu.Button {
this.widget = this._createText(item['text']);
if (this.widget !== null) {
this.signals.connectWidgetSignals(this.widget);
this.signalManager.connectWidgetSignals(this.widget);
this.add_child(this.widget);
}
} else
@@ -320,7 +348,7 @@ class MonitorWidget extends PanelMenu.Button {
}
if (prevIcon) {
this.signals.disconnectWidgetSignals(prevIcon);
this.signalManager.disconnectWidgetSignals(prevIcon);
this.insert_child_above(this.icon, prevIcon);
this.remove_child(prevIcon);
//delete prevIcon;
@@ -338,8 +366,8 @@ class MonitorWidget extends PanelMenu.Button {
if (this.menuItem) {
if (menuOpen)
this.menu.close();
for(let widgetIdx in this.popup_signals)
this.signals.disconnectWidgetSignals(this.popup_signals[widgetIdx]);
for(let widgetIdx in this.popup_widgets)
this.signalManager.disconnectWidgetSignals(this.popup_widgets[widgetIdx]);
this.menu.removeAll();
//delete this.menuItem;
}
@@ -349,7 +377,7 @@ class MonitorWidget extends PanelMenu.Button {
this.menu.open(true);
}
this.signals.updateSignals(item);
this.signalManager.updateSignals(item);
}
openPopup() {
@@ -366,6 +394,7 @@ class MonitorWidget extends PanelMenu.Button {
destroy() {
this.menu.close();
this.signalManager.destroy();
super.destroy();
}
@@ -381,6 +410,8 @@ class MonitorWidget extends PanelMenu.Button {
}
let widgets = [];
this.popup_signals = new WeakMap();
this.popup_widgets = new Array();
for (let itemIndex in item['items']) {
let widget = null;
let widgetDict = item['items'][itemIndex];
@@ -388,7 +419,7 @@ class MonitorWidget extends PanelMenu.Button {
let nestedItem = null;
if (widgetDict.hasOwnProperty('text')) {
nestedItem = widgetDict['text'];
widget = this._createText(nestedItem);
widget = this._createLabel(nestedItem);
} else if (widgetDict.hasOwnProperty('picture')) {
nestedItem = widgetDict['picture'];
widget = this._createPicture(nestedItem);
@@ -404,8 +435,9 @@ class MonitorWidget extends PanelMenu.Button {
const name = hashGet(nestedItem, 'name', '');
this.popup_signals[widget] = new SignalMgt(nestedItem, name,
this.fullname, this.dbus,
this.menu);
this);
this.popup_signals[widget].connectWidgetSignals(widget);
this.popup_widgets.push(widget);
}
if (widgets.length > 0) {
@@ -426,7 +458,7 @@ class MonitorWidget extends PanelMenu.Button {
return this._createText(itemValues);
}
_createText(item) {
__createText(item, isLabel) {
if (!item.hasOwnProperty('text')) {
log('Text must have a \'text\' value');
return null;
@@ -439,13 +471,25 @@ class MonitorWidget extends PanelMenu.Button {
if (item['text'] === '') {
return null;
} else {
const widget = new St.Button({ label: item['text'] });
let widget = null;
if (isLabel)
widget = new St.Label({ text: item['text'] });
else
widget = new St.Button({ label: item['text'] });
widget.set_style(style);
return widget;
}
}
_createText(item) {
return this.__createText(item, false);
}
_createLabel(item) {
return this.__createText(item, true);
}
_createIconOld(item) {
var itemValues = {};
itemValues = { 'path':item['icon'] };
@@ -513,14 +557,12 @@ class MonitorWidget extends PanelMenu.Button {
// From https://github.com/ubuntu/gnome-shell-extension-appindicator/blob/master/interfaces.js
// loads a xml file into an in-memory string
function loadInterfaceXml(filename) {
const extension = imports.misc.extensionUtils.getCurrentExtension();
function loadInterfaceXml(extension, filename) {
const interfacesDir = extension.dir.get_child('.');
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) {
// HACK: The "" + trick is important as hell because file_get_contents returns
@@ -529,7 +571,10 @@ function loadInterfaceXml(filename) {
// is no `XML` on very recent SpiderMonkey releases (or, if SpiderMonkey is old enough,
// will spit out a TypeError soon).
if (contents instanceof Uint8Array)
contents = imports.byteArray.toString(contents);
{
const decoder = new TextDecoder();
contents = decoder.decode(contents);
}
const res = `<node>${contents}</node>`;
return res;
} else {
@@ -538,11 +583,11 @@ function loadInterfaceXml(filename) {
}
class GenericMonitorDBUS {
constructor() {
constructor(extension) {
this.monitor_groups = {};
this.actor_clicked = {};
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.emit_signal('onActivate', null);
}
@@ -745,7 +790,7 @@ class GenericMonitorDBUS {
monitorWidget.togglePopup();
}
destructor() {
destroy() {
this._dbusImpl.emit_signal('onDeactivate', null);
for (let groupIndex in this.monitor_groups) {
const group = this.monitor_groups[groupIndex];
@@ -757,26 +802,21 @@ class GenericMonitorDBUS {
}
}
class Extension {
export default class GenericMonitorExtension extends Extension.Extension {
constructor(...args) {
super(...args);
this.textDBusService = null;
}
enable() {
this.textDBusService = new GenericMonitorDBUS();
this.textDBusService = new GenericMonitorDBUS(this);
}
disable() {
this.textDBusService.destructor();
delete this.textDBusService;
if (this.textDBusService !== null) {
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,11 +2,11 @@
"uuid": "generic-monitor@gnome-shell-extensions",
"name": "Generic Monitor",
"description": "Display text & icon on systray using DBUS",
"version": "4",
"version": "15",
"shell-version": [
"3.38",
"3.36",
"3.34"
"47",
"46",
"45"
],
"url": "http://indefero.soutade.fr/p/genericmonitor"
"url": "https://forge.soutade.fr/soutade/GnomeShellGenericMonitor"
}