Newer
Older
#!/usr/bin/env python
__version__ = '1.2'

Alberto LIVIO BECCARIA
committed
import sys
import atexit
import os
import time
import base64
import stat
import pwd
import argparse
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Notify', '0.7')
gi.require_version('AppIndicator3', '0.1')
from gi.repository import Notify
from gi.repository import Gtk
from gi.repository import GLib
"""
--------------------------------------------------------------------------------
Config
--------------------------------------------------------------------------------
"""
APP_NAME = "Labsync Tray GTK"
APP_YEAR = "2024"
APP_VERSION = __version__ + '-' + APP_YEAR
APP_DESCR = "Notifica lo stato di aggiornamento del software nella cartella /opt"
APP_AUTHORS = ["""Sezione di Informatica
DiSIT
Università del Piemonte Orientale"""]
APP_ID = "labsync-tray-gtk"
# force using StatusIcon over AppIndicator

Alberto LIVIO BECCARIA
committed
FORCE_TRAY = False
# not used in this version
#CURR_DIR = os.path.dirname(os.path.abspath(__file__))

Alberto LIVIO BECCARIA
committed
# files do not get deleted on user logout, so better create them in a neutral
# location with 666 file permission (see function create_icons_files() below)
#TMP_DIR = "/var/tmp/" + pwd.getpwuid(os.getuid()).pw_name

Alberto LIVIO BECCARIA
committed
TMP_DIR = "/var/tmp/labsync-tray"
# state names
APP_OFF = 'Off'
APP_SYNC = "Syncing"
APP_SYNCED = "Synced"

Alberto LIVIO BECCARIA
committed
# icons: could be PNG or SVG as well
APP_ICONS = {
APP_OFF: os.path.join(TMP_DIR, 'labsync-off.svg'),
APP_SYNCED: os.path.join(TMP_DIR, 'labsync-synced.svg'),
APP_SYNC: os.path.join(TMP_DIR, 'labsync-sync.svg')

Alberto LIVIO BECCARIA
committed
}

Alberto LIVIO BECCARIA
committed
CHECK_INTERVAL = 30
CHECK_INTERVAL_UNIT = 's' # seconds, according to previous value

Alberto LIVIO BECCARIA
committed
# how long the notification stays on the screen (milliseconds; 0=until user clicks)
MSG_SYNC_TITLE = "labsync"
MSG_SYNC_TEXT = """Stiamo aggiornando il software nella cartella /opt; ci \
sarà un po' di attività sul disco.\
\r\rAlcuni dei programmi lì presenti (comprese le macchine virtuali) potrebbero \
non funzionare correttamente fino al termine degli aggiornamenti.\
\r\rIl termine della procedura di aggiornamento ti verrà notificato, ti \
preghiamo di attendere qualche minuto.\
\r\r<b>Se hai atteso più di mezz'ora, non hai ricevuto la notifica e il \
software che vuoi usare continua a non funzionare, contatta i tecnici.</b>"""
MSG_SYNC_ICON="dialog-warning"
MSG_SYNC_URGENCY = 2
MSG_SYNCED_TITLE = 'labsync'
MSG_SYNCED_TEXT = "Il software nella cartella /opt è aggiornato."
MSG_SYNCED_ICON="dialog-information"
MSG_SYNCED_URGENCY = 1
# file to check for saying if we are syncing or not

Alberto LIVIO BECCARIA
committed
STR_STATUS = "Stato"
STR_ABOUT = "Info"
STR_EXIT = "Esci"
STR_CLOSE = "Chiudi"
"""
--------------------------------------------------------------------------------
Icons
--------------------------------------------------------------------------------
"""

Alberto LIVIO BECCARIA
committed
APP_ICONS_DATA = {
APP_OFF: """\

Alberto LIVIO BECCARIA
committed
PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTkyIDE5MiIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCI+PGcgdHJhbnNmb3JtPSIiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTk2LDE5MmMtNTMuMDE5MzQsMCAtOTYsLTQyLjk4MDY2IC05NiwtOTZ2MGMwLC01My4wMTkzNCA0Mi45ODA2NiwtOTYgOTYsLTk2djBjNTMuMDE5MzQsMCA5Niw0Mi45ODA2NiA5Niw5NnYwYzAsNTMuMDE5MzQgLTQyLjk4MDY2LDk2IC05Niw5NnoiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSJNOTYsMTg4LjE2Yy01MC44OTg1NiwwIC05Mi4xNiwtNDEuMjYxNDQgLTkyLjE2LC05Mi4xNnYwYzAsLTUwLjg5ODU2IDQxLjI2MTQ0LC05Mi4xNiA5Mi4xNiwtOTIuMTZ2MGM1MC44OTg1NiwwIDkyLjE2LDQxLjI2MTQ0IDkyLjE2LDkyLjE2djBjMCw1MC44OTg1NiAtNDEuMjYxNDQsOTIuMTYgLTkyLjE2LDkyLjE2eiIgZmlsbD0ibm9uZSIvPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTMuODQsMTg4LjE2di0xODQuMzJoMTg0LjMydjE4NC4zMnoiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48ZyBpZD0iTGF5ZXJfMSI+PGc+PGc+PGcgZmlsbD0iIzk1YTVhNiI+PGc+PHBhdGggZD0iTTIuOTYyNSw5NmMwLC01MS4zNzUgNDEuNjYyNSwtOTMuMDM3NSA5My4wMzc1LC05My4wMzc1YzUxLjM3NSwwIDkzLjAzNzUsNDEuNjYyNSA5My4wMzc1LDkzLjAzNzVjMCw1MS4zNzUgLTQxLjY2MjUsOTMuMDM3NSAtOTMuMDM3NSw5My4wMzc1Yy01MS4zNzUsMCAtOTMuMDM3NSwtNDEuNjYyNSAtOTMuMDM3NSwtOTMuMDM3NXoiLz48L2c+PC9nPjxwYXRoIGQ9Ik0xNjMuNTc1LDY3LjUzNzVjLTMuODYyNSwwIC03Ljc2MjUsMC4wMzc1IC0xMS42MjUsMC4wMzc1Yy04LjczNzUsLTE5LjU3NSAtMjUuODc1LC0zMy43MTI1IC00Ny40NzUsLTM3LjA4NzVjLTI0LjE1LC0zLjc4NzUgLTQ3LjIxMjUsNy4zODc1IC02MC45NzUsMjcuMTEyNWMtNi4xMTI1LDguNzM3NSA4LjI1LDE2Ljk1IDE0LjMyNSw4LjI1YzE4Ljg2MjUsLTI3IDU5LjE3NSwtMjQuNTYyNSA3NS43MTI1LDEuNzYyNWMtMy42LDAgLTcuMTYyNSwwIC0xMC43NjI1LDAuMDM3NWMtMS4wMTI1LDAgLTEuNzI1LDAuNDUgLTIuMTM3NSwxLjA4NzVjLTAuNzEyNSwwLjc4NzUgLTEuMDEyNSwxLjkxMjUgLTAuMjI1LDMuMDc1YzYuODI1LDEwLjU3NSAxMy42NSwyMS4xODc1IDIwLjQ3NSwzMS43NjI1YzEuMTYyNSwxLjc2MjUgMy42Mzc1LDEuNzYyNSA0Ljc2MjUsMGM2Ljc4NzUsLTEwLjYxMjUgMTMuNTM3NSwtMjEuMjYyNSAyMC4zMjUsLTMxLjg3NWMxLjA4NzUsLTEuOCAtMC40MTI1LC00LjE2MjUgLTIuNCwtNC4xNjI1eiIgZmlsbD0iI2ZmZmZmZiIvPjwvZz48cGF0aCBkPSJNMTM0LjM2MjUsMTI1LjY2MjVjLTE4LjcxMjUsMjYuODEyNSAtNTguNTM3NSwyNC42IC03NS4zMzc1LC0xLjEyNWMzLjQ1LDAgNi44NjI1LDAgMTAuMzEyNSwtMC4wMzc1YzEuMDEyNSwwIDEuNzI1LC0wLjQ1IDIuMTM3NSwtMS4wODc1YzAuNzEyNSwtMC43ODc1IDEuMDEyNSwtMS45MTI1IDAuMjI1LC0zLjA3NWMtNi44MjUsLTEwLjU3NSAtMTMuNjUsLTIxLjE4NzUgLTIwLjQ3NSwtMzEuNzYyNWMtMS4xNjI1LC0xLjggLTMuNjM3NSwtMS43NjI1IC00Ljc2MjUsMGMtNi43ODc1LDEwLjYxMjUgLTEzLjUzNzUsMjEuMjYyNSAtMjAuMzI1LDMxLjg3NWMtMS4xMjUsMS43NjI1IDAuMzM3NSw0LjEyNSAyLjM2MjUsNC4xMjVjNC4wMTI1LDAgNy45ODc1LC0wLjAzNzUgMTIsLTAuMDM3NWM4LjgxMjUsMTkuMjM3NSAyNS44LDMzLjExMjUgNDcuMjEyNSwzNi40ODc1YzI0LjExMjUsMy43ODc1IDQ3LjE3NSwtNy40MjUgNjAuOTM3NSwtMjcuMTEyNWM2LjExMjUsLTguNzM3NSAtOC4yNSwtMTYuOTEyNSAtMTQuMjg3NSwtOC4yNXoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PC9nPjxnPjxnIGlkPSJMYXllcl8xIj48cGF0aCBkPSJNMTg4LjgsMTUyLjU1NjhjMCwxOS44NzggLTE2LjEyMiwzNiAtMzYsMzZjLTE5Ljg3OCwwIC0zNiwtMTYuMTIyIC0zNiwtMzZjMCwtMTkuODc4IDE2LjEyMiwtMzYgMzYsLTM2YzE5Ljg3OCwwIDM2LDE2LjEyMiAzNiwzNiIgZmlsbD0iIzQwNDA0MSIvPjxwYXRoIGQ9Ik0xODIuOCwxNTIuNTU2OGMwLDE2LjU2NiAtMTMuNDM0LDMwIC0zMCwzMGMtMTYuNTY2LDAgLTMwLC0xMy40MzQgLTMwLC0zMGMwLC0xNi41NjYgMTMuNDM0LC0zMCAzMCwtMzBjMTYuNTY2LDAgMzAsMTMuNDM0IDMwLDMwIiBmaWxsPSIjZTg0ODQ5Ii8+PHBhdGggZD0iTTE0MC44LDE2Ny41NTY4Yy0wLjc2OCwwIC0xLjUzNiwtMC4yOTQgLTIuMTIxLC0wLjg3OWMtMS4xNzMsLTEuMTczIC0xLjE3MywtMy4wNjkgMCwtNC4yNDJsMjQsLTI0YzEuMTczLC0xLjE3MyAzLjA2OSwtMS4xNzMgNC4yNDIsMGMxLjE3MywxLjE3MyAxLjE3MywzLjA2OSAwLDQuMjQybC0yNCwyNGMtMC41ODUsMC41ODUgLTEuMzUzLDAuODc5IC0yLjEyMSwwLjg3OXoiIGZpbGw9IiNmZmZmZmYiLz48cGF0aCBkPSJNMTY0LjgsMTY3LjU1NjhjLTAuNzY4LDAgLTEuNTM2LC0wLjI5NCAtMi4xMjEsLTAuODc5bC0yNCwtMjRjLTEuMTczLC0xLjE3MyAtMS4xNzMsLTMuMDY5IDAsLTQuMjQyYzEuMTczLC0xLjE3MyAzLjA2OSwtMS4xNzMgNC4yNDIsMGwyNCwyNGMxLjE3MywxLjE3MyAxLjE3MywzLjA2OSAwLDQuMjQyYy0wLjU4NSwwLjU4NSAtMS4zNTMsMC44NzkgLTIuMTIxLDAuODc5eiIgZmlsbD0iI2ZmZmZmZiIvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4K""",
APP_SYNCED: """\

Alberto LIVIO BECCARIA
committed
PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTkyIDE5MiIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCI+PGcgdHJhbnNmb3JtPSIiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTk2LDE5MmMtNTMuMDE5MzQsMCAtOTYsLTQyLjk4MDY2IC05NiwtOTZ2MGMwLC01My4wMTkzNCA0Mi45ODA2NiwtOTYgOTYsLTk2djBjNTMuMDE5MzQsMCA5Niw0Mi45ODA2NiA5Niw5NnYwYzAsNTMuMDE5MzQgLTQyLjk4MDY2LDk2IC05Niw5NnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9Ik05NiwxODguMTZjLTUwLjg5ODU2LDAgLTkyLjE2LC00MS4yNjE0NCAtOTIuMTYsLTkyLjE2djBjMCwtNTAuODk4NTYgNDEuMjYxNDQsLTkyLjE2IDkyLjE2LC05Mi4xNnYwYzUwLjg5ODU2LDAgOTIuMTYsNDEuMjYxNDQgOTIuMTYsOTIuMTZ2MGMwLDUwLjg5ODU2IC00MS4yNjE0NCw5Mi4xNiAtOTIuMTYsOTIuMTZ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSJNMCwxOTJ2LTE5MmgxOTJ2MTkyeiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTMuODQsMTg4LjE2di0xODQuMzJoMTg0LjMydjE4NC4zMnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxnIGlkPSJMYXllcl8xIiBzdHJva2U9Im5vbmUiPjxnPjxnPjxnIGZpbGw9IiMzNDk4ZGIiPjxnPjxwYXRoIGQ9Ik0yLjk2MjUsOTZjMCwtNTEuMzc1IDQxLjY2MjUsLTkzLjAzNzUgOTMuMDM3NSwtOTMuMDM3NWM1MS4zNzUsMCA5My4wMzc1LDQxLjY2MjUgOTMuMDM3NSw5My4wMzc1YzAsNTEuMzc1IC00MS42NjI1LDkzLjAzNzUgLTkzLjAzNzUsOTMuMDM3NWMtNTEuMzc1LDAgLTkzLjAzNzUsLTQxLjY2MjUgLTkzLjAzNzUsLTkzLjAzNzV6Ii8+PC9nPjwvZz48cGF0aCBkPSJNMTYzLjU3NSw2Ny41Mzc1Yy0zLjg2MjUsMCAtNy43NjI1LDAuMDM3NSAtMTEuNjI1LDAuMDM3NWMtOC43Mzc1LC0xOS41NzUgLTI1Ljg3NSwtMzMuNzEyNSAtNDcuNDc1LC0zNy4wODc1Yy0yNC4xNSwtMy43ODc1IC00Ny4yMTI1LDcuMzg3NSAtNjAuOTc1LDI3LjExMjVjLTYuMTEyNSw4LjczNzUgOC4yNSwxNi45NSAxNC4zMjUsOC4yNWMxOC44NjI1LC0yNyA1OS4xNzUsLTI0LjU2MjUgNzUuNzEyNSwxLjc2MjVjLTMuNiwwIC03LjE2MjUsMCAtMTAuNzYyNSwwLjAzNzVjLTEuMDEyNSwwIC0xLjcyNSwwLjQ1IC0yLjEzNzUsMS4wODc1Yy0wLjcxMjUsMC43ODc1IC0xLjAxMjUsMS45MTI1IC0wLjIyNSwzLjA3NWM2LjgyNSwxMC41NzUgMTMuNjUsMjEuMTg3NSAyMC40NzUsMzEuNzYyNWMxLjE2MjUsMS43NjI1IDMuNjM3NSwxLjc2MjUgNC43NjI1LDBjNi43ODc1LC0xMC42MTI1IDEzLjUzNzUsLTIxLjI2MjUgMjAuMzI1LC0zMS44NzVjMS4wODc1LC0xLjggLTAuNDEyNSwtNC4xNjI1IC0yLjQsLTQuMTYyNXoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PHBhdGggZD0iTTEzNC4zNjI1LDEyNS42NjI1Yy0xOC43MTI1LDI2LjgxMjUgLTU4LjUzNzUsMjQuNiAtNzUuMzM3NSwtMS4xMjVjMy40NSwwIDYuODYyNSwwIDEwLjMxMjUsLTAuMDM3NWMxLjAxMjUsMCAxLjcyNSwtMC40NSAyLjEzNzUsLTEuMDg3NWMwLjcxMjUsLTAuNzg3NSAxLjAxMjUsLTEuOTEyNSAwLjIyNSwtMy4wNzVjLTYuODI1LC0xMC41NzUgLTEzLjY1LC0yMS4xODc1IC0yMC40NzUsLTMxLjc2MjVjLTEuMTYyNSwtMS44IC0zLjYzNzUsLTEuNzYyNSAtNC43NjI1LDBjLTYuNzg3NSwxMC42MTI1IC0xMy41Mzc1LDIxLjI2MjUgLTIwLjMyNSwzMS44NzVjLTEuMTI1LDEuNzYyNSAwLjMzNzUsNC4xMjUgMi4zNjI1LDQuMTI1YzQuMDEyNSwwIDcuOTg3NSwtMC4wMzc1IDEyLC0wLjAzNzVjOC44MTI1LDE5LjIzNzUgMjUuOCwzMy4xMTI1IDQ3LjIxMjUsMzYuNDg3NWMyNC4xMTI1LDMuNzg3NSA0Ny4xNzUsLTcuNDI1IDYwLjkzNzUsLTI3LjExMjVjNi4xMTI1LC04LjczNzUgLTguMjUsLTE2LjkxMjUgLTE0LjI4NzUsLTguMjV6IiBmaWxsPSIjZmZmZmZmIi8+PC9nPjwvZz48ZyBzdHJva2U9Im5vbmUiPjxnIGlkPSJMYXllcl8xIj48cGF0aCBkPSJNMTg4LjgsMTUyLjU1NjhjMCwxOS44NzggLTE2LjEyMiwzNiAtMzYsMzZjLTE5Ljg3OCwwIC0zNiwtMTYuMTIyIC0zNiwtMzZjMCwtMTkuODc4IDE2LjEyMiwtMzYgMzYsLTM2YzE5Ljg3OCwwIDM2LDE2LjEyMiAzNiwzNiIgZmlsbD0iIzQwNDA0MSIvPjxwYXRoIGQ9Ik0xODIuOCwxNTIuNTU2OGMwLDE2LjU2NiAtMTMuNDM0LDMwIC0zMCwzMGMtMTYuNTY2LDAgLTMwLC0xMy40MzQgLTMwLC0zMGMwLC0xNi41NjYgMTMuNDM0LC0zMCAzMCwtMzBjMTYuNTY2LDAgMzAsMTMuNDM0IDMwLDMwIiBmaWxsPSIjMWZhODViIi8+PHBhdGggZD0iTTE0OS44LDE2Ny41NTY4Yy0wLjc2NzYyLDAgLTEuNTM1MjUsLTAuMjkyODcgLTIuMTIxLC0wLjg3OWwtMTIsLTEyYy0xLjE3MTg3LC0xLjE3MTg3IC0xLjE3MTg3LC0zLjA3MDUgMCwtNC4yNDIzOGMxLjE3MTg4LC0xLjE3MTg3IDMuMDcwNSwtMS4xNzE4NyA0LjI0MjM3LDBsOS44Nzg2Myw5Ljg3OTM4bDE4Ljg3OSwtMTguODc5YzEuMTcxODgsLTEuMTcxODcgMy4wNzA1LC0xLjE3MTg3IDQuMjQyMzcsMGMxLjE3MTg4LDEuMTcxODggMS4xNzE4OCwzLjA3MDUgMCw0LjI0MjM3bC0yMSwyMWMtMC41ODYxMiwwLjU4NTc1IC0xLjM1Mzc1LDAuODc4NjIgLTIuMTIxMzgsMC44Nzg2MnoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PC9nPjxwYXRoIGQ9Ik0xMTYuOCwxODguNTU2OHYtNzJoNzJ2NzJ6IiBpZD0ib3ZlcmxheS1kcmFnIiBmaWxsPSIjZmYwMDAwIiBzdHJva2U9Im5vbmUiIG9wYWNpdHk9IjAiLz48L2c+PC9nPjwvc3ZnPgo=""",
APP_SYNC: """\

Alberto LIVIO BECCARIA
committed
PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTkyIDE5MiIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCI+PGcgdHJhbnNmb3JtPSIiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTk2LDE5MmMtNTMuMDE5MzQsMCAtOTYsLTQyLjk4MDY2IC05NiwtOTZ2MGMwLC01My4wMTkzNCA0Mi45ODA2NiwtOTYgOTYsLTk2djBjNTMuMDE5MzQsMCA5Niw0Mi45ODA2NiA5Niw5NnYwYzAsNTMuMDE5MzQgLTQyLjk4MDY2LDk2IC05Niw5NnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9Ik05NiwxODguMTZjLTUwLjg5ODU2LDAgLTkyLjE2LC00MS4yNjE0NCAtOTIuMTYsLTkyLjE2djBjMCwtNTAuODk4NTYgNDEuMjYxNDQsLTkyLjE2IDkyLjE2LC05Mi4xNnYwYzUwLjg5ODU2LDAgOTIuMTYsNDEuMjYxNDQgOTIuMTYsOTIuMTZ2MGMwLDUwLjg5ODU2IC00MS4yNjE0NCw5Mi4xNiAtOTIuMTYsOTIuMTZ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSJNMCwxOTJ2LTE5MmgxOTJ2MTkyeiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTMuODQsMTg4LjE2di0xODQuMzJoMTg0LjMydjE4NC4zMnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxnIGlkPSJMYXllcl8xIiBzdHJva2U9Im5vbmUiPjxnPjxnPjxnIGZpbGw9IiNlNjdlMjIiPjxnPjxwYXRoIGQ9Ik0yLjk2MjUsOTZjMCwtNTEuMzc1IDQxLjY2MjUsLTkzLjAzNzUgOTMuMDM3NSwtOTMuMDM3NWM1MS4zNzUsMCA5My4wMzc1LDQxLjY2MjUgOTMuMDM3NSw5My4wMzc1YzAsNTEuMzc1IC00MS42NjI1LDkzLjAzNzUgLTkzLjAzNzUsOTMuMDM3NWMtNTEuMzc1LDAgLTkzLjAzNzUsLTQxLjY2MjUgLTkzLjAzNzUsLTkzLjAzNzV6Ii8+PC9nPjwvZz48cGF0aCBkPSJNMTYzLjU3NSw2Ny41Mzc1Yy0zLjg2MjUsMCAtNy43NjI1LDAuMDM3NSAtMTEuNjI1LDAuMDM3NWMtOC43Mzc1LC0xOS41NzUgLTI1Ljg3NSwtMzMuNzEyNSAtNDcuNDc1LC0zNy4wODc1Yy0yNC4xNSwtMy43ODc1IC00Ny4yMTI1LDcuMzg3NSAtNjAuOTc1LDI3LjExMjVjLTYuMTEyNSw4LjczNzUgOC4yNSwxNi45NSAxNC4zMjUsOC4yNWMxOC44NjI1LC0yNyA1OS4xNzUsLTI0LjU2MjUgNzUuNzEyNSwxLjc2MjVjLTMuNiwwIC03LjE2MjUsMCAtMTAuNzYyNSwwLjAzNzVjLTEuMDEyNSwwIC0xLjcyNSwwLjQ1IC0yLjEzNzUsMS4wODc1Yy0wLjcxMjUsMC43ODc1IC0xLjAxMjUsMS45MTI1IC0wLjIyNSwzLjA3NWM2LjgyNSwxMC41NzUgMTMuNjUsMjEuMTg3NSAyMC40NzUsMzEuNzYyNWMxLjE2MjUsMS43NjI1IDMuNjM3NSwxLjc2MjUgNC43NjI1LDBjNi43ODc1LC0xMC42MTI1IDEzLjUzNzUsLTIxLjI2MjUgMjAuMzI1LC0zMS44NzVjMS4wODc1LC0xLjggLTAuNDEyNSwtNC4xNjI1IC0yLjQsLTQuMTYyNXoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PHBhdGggZD0iTTEzNC4zNjI1LDEyNS42NjI1Yy0xOC43MTI1LDI2LjgxMjUgLTU4LjUzNzUsMjQuNiAtNzUuMzM3NSwtMS4xMjVjMy40NSwwIDYuODYyNSwwIDEwLjMxMjUsLTAuMDM3NWMxLjAxMjUsMCAxLjcyNSwtMC40NSAyLjEzNzUsLTEuMDg3NWMwLjcxMjUsLTAuNzg3NSAxLjAxMjUsLTEuOTEyNSAwLjIyNSwtMy4wNzVjLTYuODI1LC0xMC41NzUgLTEzLjY1LC0yMS4xODc1IC0yMC40NzUsLTMxLjc2MjVjLTEuMTYyNSwtMS44IC0zLjYzNzUsLTEuNzYyNSAtNC43NjI1LDBjLTYuNzg3NSwxMC42MTI1IC0xMy41Mzc1LDIxLjI2MjUgLTIwLjMyNSwzMS44NzVjLTEuMTI1LDEuNzYyNSAwLjMzNzUsNC4xMjUgMi4zNjI1LDQuMTI1YzQuMDEyNSwwIDcuOTg3NSwtMC4wMzc1IDEyLC0wLjAzNzVjOC44MTI1LDE5LjIzNzUgMjUuOCwzMy4xMTI1IDQ3LjIxMjUsMzYuNDg3NWMyNC4xMTI1LDMuNzg3NSA0Ny4xNzUsLTcuNDI1IDYwLjkzNzUsLTI3LjExMjVjNi4xMTI1LC04LjczNzUgLTguMjUsLTE2LjkxMjUgLTE0LjI4NzUsLTguMjV6IiBmaWxsPSIjZmZmZmZmIi8+PC9nPjwvZz48ZyBzdHJva2U9Im5vbmUiPjxnIGlkPSJMYXllcl8xIj48cGF0aCBkPSJNMTg4LjgsMTUyLjU1NjhjMCwxOS44NzggLTE2LjEyMiwzNiAtMzYsMzZjLTE5Ljg3OCwwIC0zNiwtMTYuMTIyIC0zNiwtMzZjMCwtMTkuODc4IDE2LjEyMiwtMzYgMzYsLTM2YzE5Ljg3OCwwIDM2LDE2LjEyMiAzNiwzNiIgZmlsbD0iIzQwNDA0MSIvPjxwYXRoIGQ9Ik0xNTIuOCwxMjIuNTU2OGMxNi41NjYsMCAzMCwxMy40MzQgMzAsMzBjMCwxNi41NjYgLTEzLjQzNCwzMCAtMzAsMzBjLTE2LjU2NiwwIC0zMCwtMTMuNDM0IC0zMCwtMzBjMCwtMTYuNTY2IDEzLjQzNCwtMzAgMzAsLTMwIiBmaWxsPSIjZmZmZmZmIi8+PHBhdGggZD0iTTE2NC44LDE2Ny41NTY4Yy0wLjc2OCwwIC0xLjUzNiwtMC4yOTQgLTIuMTIxLC0wLjg3OWwtMTIsLTEyYy0wLjU2NCwtMC41NjEgLTAuODc5LC0xLjMyNiAtMC44NzksLTIuMTIxdi0yMWMwLC0xLjY1NiAxLjM0NCwtMyAzLC0zYzEuNjU2LDAgMywxLjM0NCAzLDN2MTkuNzU4bDExLjEyMSwxMS4xMjFjMS4xNzMsMS4xNzMgMS4xNzMsMy4wNjkgMCw0LjI0MmMtMC41ODUsMC41ODUgLTEuMzUzLDAuODc5IC0yLjEyMSwwLjg3OXoiIGZpbGw9IiMyNmE5ZTAiLz48Y2lyY2xlIGN4PSI0MDcuNDY2NjciIGN5PSI0MDYuODE4MTMiIHRyYW5zZm9ybT0ic2NhbGUoMC4zNzUsMC4zNzUpIiByPSIxMiIgZmlsbD0iIzI2YTllMCIvPjwvZz48L2c+PHBhdGggZD0iTTExNi44LDE4OC41NTY4di03Mmg3MnY3MnoiIGlkPSJvdmVybGF5LWRyYWciIGZpbGw9IiNmZjAwMDAiIHN0cm9rZT0ibm9uZSIgb3BhY2l0eT0iMCIvPjwvZz48L2c+PC9zdmc+Cg=="""
}
"""
--------------------------------------------------------------------------------
Main application class
--------------------------------------------------------------------------------
"""
class TrayApp():

Alberto LIVIO BECCARIA
committed
def __init__(self, appid, icon=None, menu=None, check_interval=None):
self.task = APP_SYNCED

Alberto LIVIO BECCARIA
committed
self.check_interval = CHECK_INTERVAL
# set defaults and parameters
if check_interval != None:
self.check_interval = check_interval
self.menu = self.build_menu()

Alberto LIVIO BECCARIA
committed
self.menu = menu

Alberto LIVIO BECCARIA
committed
self.icon = icon
if icon != None:
self.icon = icon
if APPINDICATOR_SUPPORT == 1 and FORCE_TRAY == False:
appid, self.icon, AppIndicator3.IndicatorCategory.APPLICATION_STATUS)
self.tray.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
self.tray.set_menu(self.menu)
else:
self.tray = Gtk.StatusIcon()
self.tray.set_visible(True);

Alberto LIVIO BECCARIA
committed
self.tray.connect('popup-menu', self.on_popup_menu)
self.tray.connect("button-press-event", self.on_mouse_click)
self.tray.set_tooltip_text(APP_NAME)
self.tray.set_title(APP_NAME)

Alberto LIVIO BECCARIA
committed
self.tray.set_from_file(self.icon)

Alberto LIVIO BECCARIA
committed
# intial icon could be different from the current state, force update
self.update_icon()
# send notification on start only if labsync is syncing
if self.task_changed:
self.send_notification()

Alberto LIVIO BECCARIA
committed
# then start updating every CHECK_INTERVAL seconds
# http://developer.gnome.org/pygobject/stable/glib-functions.html#function-glib--timeout-add-seconds

Alberto LIVIO BECCARIA
committed
GLib.timeout_add_seconds(self.check_interval, self.handler_timeout)

Alberto LIVIO BECCARIA
committed
def on_mouse_click(self, data, event):
# 1-left, 2-middle, 3-right
if event.button == 1:
self.update_task()
self.send_notification()
# time is required by the popup
# time = Gtk.get_current_event_time()
#self.menu.popup(None, None, Gtk.StatusIcon.position_menu, self.tray,
# event.button, time)

Alberto LIVIO BECCARIA
committed
def on_popup_menu(self, icon, button, time):
self.menu.popup(None, None, Gtk.StatusIcon.position_menu, icon,
button, time)
def build_menu(self):
menu = Gtk.Menu()
item_status = Gtk.MenuItem(label=STR_STATUS)
item_status.connect("activate", self.handler_menu_status)
menu.append(item_status)
about = Gtk.MenuItem(label=STR_ABOUT)
about.connect("activate", self.handler_menu_about)
menu.append(about)
menu.append(Gtk.SeparatorMenuItem())
item_quit = Gtk.MenuItem(label=STR_EXIT)
item_quit.connect("activate", self.handler_menu_quit)
menu.append(item_quit)
return menu
def handler_menu_quit(self, evt):
Gtk.main_quit()
def handler_menu_status(self, evt):
self.update_task()
self.send_notification()
def send_notification(self):
if self.task == APP_SYNC:
title = MSG_SYNC_TITLE
txt = MSG_SYNC_TEXT
icon = MSG_SYNC_ICON
urgency = MSG_SYNC_URGENCY
title = MSG_SYNCED_TITLE
txt = MSG_SYNCED_TEXT
icon = MSG_SYNCED_ICON
urgency = MSG_SYNCED_URGENCY
# close the current notification, if any
if self.current_notification != None:
self.current_notification.close()
self.current_notification = None

Alberto LIVIO BECCARIA
committed
self.current_notification = Message(None, title, txt, icon, NOTIFY_TIME, urgency, self.show_about_dialog).notification
self.current_notification.connect("closed", self.handler_current_notification_closed)
def handler_current_notification_closed(self, evt):
self.current_notification = None

Alberto LIVIO BECCARIA
committed
def show_about_dialog(*args, **kwargs):
about_dialog = Gtk.AboutDialog()
about_dialog.set_destroy_with_parent(True)
about_dialog.set_icon_name("dialog-information")
# no SVG?
about_dialog.set_logo_icon_name("")
about_dialog.set_program_name(APP_NAME)
about_dialog.set_name(APP_NAME)
about_dialog.set_version(APP_VERSION)
about_dialog.set_license_type(Gtk.License.GPL_3_0)
about_dialog.set_comments(APP_DESCR)
about_dialog.set_authors(APP_AUTHORS)
about_dialog.set_default_response(Gtk.ResponseType.CLOSE)
about_dialog.run()
about_dialog.destroy()

Alberto LIVIO BECCARIA
committed
def handler_menu_about(self, evt):
self.show_about_dialog()

Alberto LIVIO BECCARIA
committed
def update_icon(self):
if APPINDICATOR_SUPPORT == 1 and FORCE_TRAY == False:
self.tray.set_icon_full(self.icon, "-")
self.tray.set_from_file(self.icon, "-")
def update_task(self):
exists = os.path.isfile(FILE_TO_CHECK)
if exists:
task = APP_SYNCED

Alberto LIVIO BECCARIA
committed
self.icon = APP_ICONS[APP_SYNCED]
task = APP_SYNC

Alberto LIVIO BECCARIA
committed
self.icon = APP_ICONS[APP_SYNC]
if self.task != task:
self.task_changed = True

Alberto LIVIO BECCARIA
committed
self.update_icon()
else:
self.task_changed = False
self.task = task
def handler_timeout(self):
"""This will be called every few seconds by the GLib.timeout.
"""
self.update_task()
if self.task_changed:
self.send_notification()
# return True so that we get called again
# returning False will make the timeout stop
return True
def main(self):
Gtk.main()
"""
--------------------------------------------------------------------------------
Message class
--------------------------------------------------------------------------------
"""
def mess_callback(*args, **kwargs):
pass
def __init__(self, parent, caption, msg, img=None, timeout=None,

Alberto LIVIO BECCARIA
committed
urgency=None, info_btn_callback=None):
self.notification = None
if timeout != None: pass
else: timeout = 0 # message should not timeout
if urgency: pass
else: urgency = 0
caps = Notify.get_server_caps()
self.notification = Notify.Notification.new(caption, msg, img) # passing an image is optional

Alberto LIVIO BECCARIA
committed
# set the timeout in milliseconds. This does not work everywhere, it depends on
# notify server's capabilities.
self.notification.set_timeout(timeout)
self.notification.set_urgency(urgency) # 0-Low, 1-Normal, 2-Critical

Alberto LIVIO BECCARIA
committed
if 'actions' in caps:
self.notification.add_action("action_click", STR_ABOUT, info_btn_callback, None)
if timeout != 0:
# without the following `add_action` option, no countdown to the timeout is shown
self.notification.add_action("close", STR_CLOSE, self.mess_callback, None) # show the countdown to close
self.notification.show()
"""
--------------------------------------------------------------------------------
Main
--------------------------------------------------------------------------------
"""
def handler_exit():
cleanup()
def handler_signal(sig, frame):
cleanup()

Alberto LIVIO BECCARIA
committed
def initialize():
create_tmp_dir()
create_icon_files()
def cleanup():

Alberto LIVIO BECCARIA
committed
# we can skip files and folder cleanup
# the icons are always needed and never cleaned up on session logout...
#delete_icon_files()
#delete_tmp_dir()
Notify.uninit()
if Gtk.main_level() > 0:
Gtk.main_quit()
def create_tmp_dir():
if not os.path.exists(TMP_DIR):
os.makedirs(TMP_DIR)

Alberto LIVIO BECCARIA
committed
# short way to change permissions
# see create_icon_files() for alternative way
os.chmod(TMP_DIR, 0o777)
def delete_tmp_dir():
if os.path.exists(TMP_DIR):
os.rmdir(TMP_DIR)
def create_icon_files():
for key, value in APP_ICONS.items():

Alberto LIVIO BECCARIA
committed
if not os.path.isfile(value):
with open(value, 'wb') as fp:
fp.write(base64.b64decode(APP_ICONS_DATA[key]))
# these 2 command change file permissions to let everyone
# delete the files, if needed
st = os.stat(APP_ICONS[key])
# https://docs.python.org/3/library/os.html#os.chmod
# https://docs.python.org/3/library/stat.html#stat.S_IWUSR
os.chmod(APP_ICONS[key],
st.st_mode |
stat.S_IRUSR | stat.S_IWUSR |
stat.S_IRGRP | stat.S_IWGRP |
stat.S_IROTH | stat.S_IWOTH
)
def delete_icon_files():
for key, value in APP_ICONS.items():
if os.path.isfile(value):

Alberto LIVIO BECCARIA
committed
#print ("Deleted: " + value)
os.remove(value)
# detect AppIndicator support
APPINDICATOR_SUPPORT = 1
try:
from gi.repository import AppIndicator3
except:
APPINDICATOR_SUPPORT = 0

Alberto LIVIO BECCARIA
committed
if __name__ == "__main__":
# parse command line arguments
parser = argparse.ArgumentParser(description=APP_NAME + ' ' + APP_VERSION)

Alberto LIVIO BECCARIA
committed
parser.add_argument('-i', '--interval', type=int,
help='check interval (seconds)')
initialize()
# clean up on exit
atexit.register(handler_exit) # normal exit
signal.signal(signal.SIGINT, handler_signal) # ctrl-c
signal.signal(signal.SIGTERM, handler_signal) # kill signal (but not kill -9)

Alberto LIVIO BECCARIA
committed
app = TrayApp(APP_ID, icon=APP_ICONS[APP_OFF], check_interval=args.interval)
print("Check interval: " + str(app.check_interval) + CHECK_INTERVAL_UNIT)