Initial commit
This commit is contained in:
commit
d7e6db3bec
339
COPYING
Normal file
339
COPYING
Normal file
|
@ -0,0 +1,339 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc. <http://fsf.org>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
77
README.md
Normal file
77
README.md
Normal file
|
@ -0,0 +1,77 @@
|
|||
Introduction
|
||||
------------
|
||||
|
||||
This GNOME Shell Extension aims to display information to center box. Using DBUS protocol, any application can add or remove text and icons in the upper bar, right after date & clock.
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Create a symbolic link from your _.local_ directory and enable extension
|
||||
|
||||
ln -s $PWD/generic-monitor@gnome-shell-extensions/ ~/.local/share/gnome-shell/extensions/
|
||||
gnome-extensions enable generic-monitor@gnome-shell-extensions
|
||||
|
||||
Restart GNOME
|
||||
|
||||
|
||||
DBUS protocol
|
||||
-------------
|
||||
|
||||
All functions read JSON formatted parameters
|
||||
|
||||
notify():
|
||||
{
|
||||
"group": "groupname",
|
||||
// style and icon-style are optional
|
||||
// item can have text and/or icon
|
||||
"items": [
|
||||
{
|
||||
"name":"",
|
||||
"text":"",
|
||||
"style":"",
|
||||
"icon-style":"",
|
||||
"icon":"",
|
||||
}, ...
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
deleteItems():
|
||||
{
|
||||
"items": ["<itemName>@<groupName>", ...]
|
||||
}
|
||||
|
||||
deleteGroups():
|
||||
{
|
||||
"groups": ["<groupName>", ...]
|
||||
}
|
||||
|
||||
|
||||
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","text":"Hello","style":"color:green"}]}'
|
||||
gdbus call --session --dest org.gnome.Shell --object-path /com/soutade/GenericMonitor --method com.soutade.GenericMonitor.deleteGroups '{"groups":["new"]}'
|
||||
|
||||
Python example is available
|
||||
|
||||
|
||||
Development
|
||||
-----------
|
||||
|
||||
After doing code update, you can test it within a nested window. In other cases you have to restart GNOME.
|
||||
|
||||
dbus-run-session -- gnome-shell --nested [--wayland]
|
||||
|
||||
To see log & errors :
|
||||
|
||||
journalctl /usr/bin/gnome-shell
|
||||
|
||||
|
||||
Licence
|
||||
-------
|
||||
|
||||
GNU GPL 3
|
11
dbus.xml
Normal file
11
dbus.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<interface name="com.soutade.GenericMonitor">
|
||||
<method name="notify">
|
||||
<arg type="s" direction="in" />
|
||||
</method>
|
||||
<method name="deleteItems">
|
||||
<arg type="s" direction="in" />
|
||||
</method>
|
||||
<method name="deleteGroups">
|
||||
<arg type="s" direction="in" />
|
||||
</method>
|
||||
</interface>
|
203
example.py
Executable file
203
example.py
Executable file
|
@ -0,0 +1,203 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
'''
|
||||
Display number of mail from gmail account and Pidgin icon if there is some unread Pidgin message
|
||||
'''
|
||||
|
||||
import json
|
||||
import time
|
||||
import requests
|
||||
from requests.auth import HTTPBasicAuth
|
||||
import xml.dom.minidom
|
||||
import getpass
|
||||
import dbus
|
||||
from dbus.mainloop.glib import DBusGMainLoop
|
||||
import gi
|
||||
gi.require_version('Gst', '1.0')
|
||||
from gi.repository import GObject, GLib, Gst
|
||||
from threading import Thread
|
||||
from signal import signal, SIGINT
|
||||
import sys
|
||||
|
||||
MAIL_ADDRESS='XXX@gmail.com'
|
||||
|
||||
class DBUSItem:
|
||||
def __init__(self, name, text='', style='', icon='', iconStyle=''):
|
||||
self.name = name
|
||||
self.text = text
|
||||
self.style= style
|
||||
self.icon = icon
|
||||
self.iconStyle = iconStyle
|
||||
|
||||
def toMap(self):
|
||||
myMap = {"name":self.name}
|
||||
for p in ('text', 'style', 'icon'):
|
||||
if self.__dict__[p]:
|
||||
myMap[p] = self.__dict__[p]
|
||||
if self.iconStyle:
|
||||
myMap['icon-style'] = self.iconStyle
|
||||
return [myMap]
|
||||
|
||||
def getMail(user, password):
|
||||
res = DBUSItem('mail')
|
||||
|
||||
address = "https://mail.google.com/mail/feed/atom"
|
||||
auth=HTTPBasicAuth(user, password)
|
||||
req = requests.get(address, auth=auth)
|
||||
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:
|
||||
res.text = '1 msg'
|
||||
elif nb_messages > 1:
|
||||
res.text = '%d msgs' % (nb_messages)
|
||||
res.style = 'color:white'
|
||||
except Exception as e:
|
||||
res.text = str(e)
|
||||
else:
|
||||
res.text = 'Mail error %d' % (req.status_code)
|
||||
|
||||
return res.toMap()
|
||||
|
||||
def getEvents(mail_user, mail_password, proxy):
|
||||
encoder = json.JSONEncoder()
|
||||
res = {'group':'Mail', 'items':[]}
|
||||
res['items'] += getMail(mail_user, mail_password)
|
||||
proxy.notify(encoder.encode(res), dbus_interface='com.soutade.GenericMonitor')
|
||||
|
||||
class StockThread(Thread):
|
||||
SLEEP_TIME = 30
|
||||
|
||||
def stop(self):
|
||||
self._stopLoop = True
|
||||
|
||||
def run(self):
|
||||
self._stopLoop = False
|
||||
mail_password = getpass.getpass('Enter password for address %s: ' % (MAIL_ADDRESS))
|
||||
while not self._stopLoop:
|
||||
getEvents(MAIL_ADDRESS, mail_password, systray_proxy)
|
||||
# Be more reactive on signal capture
|
||||
for i in range(0, StockThread.SLEEP_TIME):
|
||||
if self._stopLoop: break
|
||||
time.sleep(1)
|
||||
|
||||
def signalHandler(signal_received, frame):
|
||||
stockThread.stop()
|
||||
mainLoop.quit()
|
||||
stockThread.join()
|
||||
encoder = json.JSONEncoder()
|
||||
res = {'groups':['Mail', 'Pidgin']}
|
||||
systray_proxy.deleteGroups(encoder.encode(res), dbus_interface='com.soutade.GenericMonitor')
|
||||
sys.exit(0)
|
||||
|
||||
PURPLE_CONV_UPDATE_UNSEEN = 4
|
||||
PURPLE_MESSAGE_SEND = 0
|
||||
|
||||
class PidginConversation:
|
||||
STATUS_SEEN = 0
|
||||
STATUS_UNSEEN = 1
|
||||
|
||||
def __init__(self, _id):
|
||||
self._id = _id
|
||||
self.status = PidginConversation.STATUS_SEEN
|
||||
self.nbMessages = 1
|
||||
|
||||
def updateNbMessages(self):
|
||||
self.nbMessages += 1
|
||||
# Status is seen until it changes
|
||||
self.status = PidginConversation.STATUS_SEEN
|
||||
|
||||
def setSeen(self):
|
||||
self.status = PidginConversation.STATUS_SEEN
|
||||
|
||||
def setUnseen(self):
|
||||
self.nbMessages = 0
|
||||
self.status = PidginConversation.STATUS_UNSEEN
|
||||
|
||||
def isSeen(self):
|
||||
return (self.status == PidginConversation.STATUS_SEEN)
|
||||
|
||||
pidgin_conversations = {}
|
||||
|
||||
def pidginMessageReceived(account, sender, message, conversation, flags):
|
||||
pidginConversation = pidgin_conversations.get(conversation, None)
|
||||
if not pidginConversation:
|
||||
pidgin_conversations[conversation] = PidginConversation(conversation)
|
||||
else:
|
||||
pidginConversation.updateNbMessages()
|
||||
|
||||
def pidginMessageWrote(account, sender, message, conversation, flags):
|
||||
if not (flags & (1 << PURPLE_MESSAGE_SEND)):
|
||||
return
|
||||
pidginConversation = pidgin_conversations.get(conversation, None)
|
||||
if not pidginConversation:
|
||||
pidgin_conversations[conversation] = PidginConversation(conversation)
|
||||
pidginConversation = pidgin_conversations[conversation]
|
||||
pidginConversation.setSeen()
|
||||
pidginConversation.nbMessages = 0
|
||||
|
||||
def pidginConversationUpdated(conversation, _type):
|
||||
if _type != PURPLE_CONV_UPDATE_UNSEEN:
|
||||
return
|
||||
|
||||
pidginConversation = pidgin_conversations.get(conversation, None)
|
||||
if not pidginConversation:
|
||||
return
|
||||
|
||||
if pidginConversation.isSeen():
|
||||
if pidginConversation.nbMessages > 0:
|
||||
pidginConversation.setUnseen()
|
||||
else:
|
||||
pidginConversation.setSeen()
|
||||
|
||||
encoder = json.JSONEncoder()
|
||||
# Message not seen by user
|
||||
if not pidginConversation.isSeen():
|
||||
res = {'group':'Pidgin', 'items':[]}
|
||||
res['items'] += DBUSItem('pidgin', icon='/usr/share/icons/hicolor/22x22/apps/pidgin.png', iconStyle='icon-size:22px').toMap()
|
||||
systray_proxy.notify(encoder.encode(res), dbus_interface='com.soutade.GenericMonitor')
|
||||
else:
|
||||
deleteIcon = True
|
||||
# Are all messages seen ?
|
||||
for pConv in pidgin_conversations.values():
|
||||
if not pConv.isSeen():
|
||||
deleteIcon = False
|
||||
break
|
||||
if deleteIcon:
|
||||
res = {'items':['pidgin@Pidgin']}
|
||||
systray_proxy.deleteItems(encoder.encode(res), dbus_interface='com.soutade.GenericMonitor')
|
||||
|
||||
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
|
||||
|
||||
bus = dbus.SessionBus()
|
||||
systray_proxy = bus.get_object('org.gnome.Shell', '/com/soutade/GenericMonitor')
|
||||
|
||||
bus.add_signal_receiver(pidginMessageReceived, 'ReceivedImMsg', 'im.pidgin.purple.PurpleInterface')
|
||||
bus.add_signal_receiver(pidginMessageReceived, 'ReceivedChatMsg', 'im.pidgin.purple.PurpleInterface')
|
||||
bus.add_signal_receiver(pidginMessageWrote, 'WroteImMsg', 'im.pidgin.purple.PurpleInterface')
|
||||
bus.add_signal_receiver(pidginMessageWrote, 'WroteChatMsg', 'im.pidgin.purple.PurpleInterface')
|
||||
bus.add_signal_receiver(pidginConversationUpdated, 'ConversationUpdated', 'im.pidgin.purple.PurpleInterface')
|
||||
|
||||
stockThread = StockThread()
|
||||
stockThread.start()
|
||||
|
||||
signal(SIGINT, signalHandler)
|
||||
|
||||
mainLoop = GLib.MainLoop()
|
||||
mainLoop.run()
|
311
extension.js
Normal file
311
extension.js
Normal file
|
@ -0,0 +1,311 @@
|
|||
/* extension.js
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
/* Based on https://stackoverflow.com/questions/33001192/how-to-send-a-string-to-a-gnome-shell-extension */
|
||||
|
||||
const St = imports.gi.St;
|
||||
const Gio = imports.gi.Gio;
|
||||
const Lang = imports.lang;
|
||||
const Main = imports.ui.main;
|
||||
|
||||
class MonitorWidget {
|
||||
constructor(name, group, text, style, icon, iconStyle) {
|
||||
this.name = name;
|
||||
this.group = group;
|
||||
this._createIcon(icon, iconStyle);
|
||||
this._createText(text, style);
|
||||
}
|
||||
|
||||
_createText(text, style)
|
||||
{
|
||||
this.style = style;
|
||||
if (text == "")
|
||||
this.widget = null;
|
||||
else
|
||||
{
|
||||
this.widget = new St.Button({ label: text});
|
||||
this.widget.set_style(this.style);
|
||||
}
|
||||
}
|
||||
|
||||
_createIcon(icon, style)
|
||||
{
|
||||
this.iconStyle = style;
|
||||
if (icon == "")
|
||||
this.icon = null;
|
||||
else
|
||||
{
|
||||
let gicon = Gio.icon_new_for_string(icon);
|
||||
this.icon = new St.Icon({ gicon });
|
||||
this.icon.set_style(this.iconStyle);
|
||||
}
|
||||
}
|
||||
|
||||
update(text, style, icon, iconStyle)
|
||||
{
|
||||
if (!this.widget)
|
||||
this._createText(text, style)
|
||||
else
|
||||
{
|
||||
if (text != "")
|
||||
this.widget.label = text;
|
||||
this.style = style;
|
||||
this.widget.set_style(this.style);
|
||||
}
|
||||
|
||||
if (icon != "")
|
||||
this._createIcon(icon, iconStyle);
|
||||
else
|
||||
{
|
||||
if (iconStyle != "" && this.icon)
|
||||
{
|
||||
this.iconStyle = style;
|
||||
this.icon.set_style(this.iconStyle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
let extension = imports.misc.extensionUtils.getCurrentExtension();
|
||||
|
||||
let interfaces_dir = extension.dir.get_child(".")
|
||||
|
||||
let file = interfaces_dir.get_child(filename);
|
||||
|
||||
let [ result, contents ] = imports.gi.GLib.file_get_contents(file.get_path());
|
||||
|
||||
if (result) {
|
||||
//HACK: The "" + trick is important as hell because file_get_contents returns
|
||||
// an object (WTF?) but Gio.makeProxyWrapper requires `typeof() == "string"`
|
||||
// Otherwise, it will try to check `instanceof XML` and fail miserably because there
|
||||
// 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);
|
||||
return "<node>" + contents + "</node>";
|
||||
} else {
|
||||
throw new Error("Generic monitor: Could not load file: "+filename);
|
||||
}
|
||||
}
|
||||
|
||||
const GenericMonitorDBUS = new Lang.Class({
|
||||
Name: 'GenericMonitor',
|
||||
|
||||
_init: function() {
|
||||
this.monitor_groups = {};
|
||||
this.box = Main.panel._centerBox;
|
||||
this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(loadInterfaceXml('dbus.xml'), this);
|
||||
this._dbusImpl.export(Gio.DBus.session, '/com/soutade/GenericMonitor');
|
||||
},
|
||||
|
||||
_checkParmeters: function(parameters)
|
||||
{
|
||||
if (!parameters.hasOwnProperty("group"))
|
||||
throw new Error("No group defined");
|
||||
|
||||
if (!parameters.hasOwnProperty("items"))
|
||||
throw new Error("No items defined");
|
||||
|
||||
for (let itemIndex in parameters["items"]) {
|
||||
let item = parameters["items"][itemIndex];
|
||||
if (!item.hasOwnProperty("name"))
|
||||
throw new Error("No name defined for item");
|
||||
if (!item.hasOwnProperty("text") && !item.hasOwnProperty("icon"))
|
||||
throw new Error("No text not icon defined for item");
|
||||
}
|
||||
},
|
||||
|
||||
_getItemFromGroup: function(group, name)
|
||||
{
|
||||
for (let groupItemIndex in group)
|
||||
{
|
||||
let groupItem = group[groupItemIndex];
|
||||
if (groupItem.name == name)
|
||||
return groupItem;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
notify: function(str) {
|
||||
let parameters = JSON.parse(str);
|
||||
this._checkParmeters(parameters);
|
||||
|
||||
let groupName = parameters["group"];
|
||||
let group;
|
||||
if (!this.monitor_groups.hasOwnProperty(groupName))
|
||||
{
|
||||
group = [];
|
||||
this.monitor_groups[groupName] = group;
|
||||
}
|
||||
else
|
||||
group = this.monitor_groups[groupName];
|
||||
|
||||
for (let itemIndex in parameters["items"]) {
|
||||
let item = parameters["items"][itemIndex];
|
||||
let style = "";
|
||||
if (item.hasOwnProperty("style"))
|
||||
style = item["style"];
|
||||
let text = "";
|
||||
if (item.hasOwnProperty("text"))
|
||||
text = item["text"];
|
||||
let icon = "";
|
||||
if (item.hasOwnProperty("icon"))
|
||||
icon = item["icon"];
|
||||
let iconStyle = "";
|
||||
if (item.hasOwnProperty("icon-style"))
|
||||
iconStyle = item["icon-style"];
|
||||
|
||||
let monitorWidget = this._getItemFromGroup(group, item["name"]);
|
||||
let lastWidget = null;
|
||||
|
||||
// New widget
|
||||
if (monitorWidget == null)
|
||||
{
|
||||
monitorWidget = new MonitorWidget(item["name"], groupName, text, style, icon, iconStyle);
|
||||
if (group.length)
|
||||
lastWidget = group[group.length-1].widget;
|
||||
group.push(monitorWidget);
|
||||
// lastWidget => NULL, insert at the end
|
||||
if (monitorWidget.icon)
|
||||
{
|
||||
this.box.insert_child_above(monitorWidget.icon, lastWidget);
|
||||
lastWidget = monitorWidget.icon;
|
||||
}
|
||||
if (monitorWidget.widget)
|
||||
this.box.insert_child_above(monitorWidget.widget, lastWidget);
|
||||
}
|
||||
else
|
||||
{
|
||||
let prevWidget = monitorWidget.widget;
|
||||
let prevIcon = monitorWidget.icon;
|
||||
|
||||
monitorWidget.update(text, style, icon, iconStyle);
|
||||
|
||||
if (monitorWidget.icon)
|
||||
{
|
||||
if (prevIcon)
|
||||
this.box.remove_child(prevIcon);
|
||||
this.box.insert_child_above(monitorWidget.icon, lastWidget);
|
||||
lastWidget = monitorWidget.icon;
|
||||
}
|
||||
|
||||
if (!prevWidget && monitorWidget.widget)
|
||||
this.box.insert_child_above(monitorWidget.widget, lastWidget);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_removeItemFromBox: function(item) {
|
||||
if (item.widget)
|
||||
this.box.remove_child(item.widget);
|
||||
if (item.icon)
|
||||
this.box.remove_child(item.icon);
|
||||
},
|
||||
|
||||
deleteItems: function(str) {
|
||||
let parameters = JSON.parse(str);
|
||||
|
||||
if (!parameters.hasOwnProperty("items"))
|
||||
throw new Error("No items defined");
|
||||
|
||||
for (let itemIndex in parameters["items"]) {
|
||||
let itemName = parameters["items"][itemIndex];
|
||||
let fullName = itemName.split("@");
|
||||
if (fullName.length != 2)
|
||||
throw new Error("Invalid name " + itemName);
|
||||
itemName = fullName[0];
|
||||
let groupName = fullName[1];
|
||||
if (!this.monitor_groups.hasOwnProperty(groupName))
|
||||
continue;
|
||||
let group = this.monitor_groups[groupName];
|
||||
let item = this._getItemFromGroup(group, itemName);
|
||||
if (item != null)
|
||||
{
|
||||
this._removeItemFromBox(item);
|
||||
delete group[item["name"]];
|
||||
if (group.length == 0)
|
||||
delete this.monitor_groups[groupName];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
deleteGroups: function(str) {
|
||||
let parameters = JSON.parse(str);
|
||||
|
||||
if (!parameters.hasOwnProperty("groups"))
|
||||
throw new Error("No groups defined");
|
||||
|
||||
let groupsToDelete = [];
|
||||
for (let groupIndex in parameters["groups"]) {
|
||||
groupName = parameters["groups"][groupIndex];
|
||||
if (!this.monitor_groups.hasOwnProperty(groupName))
|
||||
continue;
|
||||
let group = this.monitor_groups[groupName];
|
||||
for (let itemIndex in group) {
|
||||
this._removeItemFromBox(group[itemIndex]);
|
||||
}
|
||||
groupsToDelete.push(groupName);
|
||||
}
|
||||
for(let groupDeleteIndex in groupsToDelete)
|
||||
{
|
||||
let groupName = groupsToDelete[groupDeleteIndex];
|
||||
delete this.monitor_groups[groupName];
|
||||
}
|
||||
},
|
||||
|
||||
deleteAll: function() {
|
||||
for (let groupIndex in this.monitor_groups) {
|
||||
let group = this.monitor_groups[groupIndex];
|
||||
for (let itemIndex in group) {
|
||||
this._removeItemFromBox(group[itemIndex]);
|
||||
}
|
||||
}
|
||||
this.monitor_groups = {}
|
||||
},
|
||||
});
|
||||
|
||||
class Extension {
|
||||
constructor() {
|
||||
this.textDBusService = new GenericMonitorDBUS();
|
||||
}
|
||||
|
||||
enable() {
|
||||
}
|
||||
|
||||
disable() {
|
||||
this.textDBusService.deleteAll();
|
||||
}
|
||||
}
|
||||
|
||||
let extension = null;
|
||||
|
||||
function init() {
|
||||
extension = new Extension();
|
||||
}
|
||||
|
||||
function enable() {
|
||||
extension.enable();
|
||||
}
|
||||
|
||||
function disable() {
|
||||
extension.disable();
|
||||
}
|
10
metadata.json
Normal file
10
metadata.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"uuid": "generic-monitor@gnome-shell-extensions",
|
||||
"name": "Generic Monitor",
|
||||
"description": "Generic monitor using DBUS",
|
||||
"version": "1",
|
||||
"shell-version": [
|
||||
"3.34"
|
||||
],
|
||||
"url": "http://indefero.soutade.fr/p/genericmonitor"
|
||||
}
|
1
stylesheet.css
Normal file
1
stylesheet.css
Normal file
|
@ -0,0 +1 @@
|
|||
/* Add your custom extension styling here */
|
Loading…
Reference in New Issue
Block a user