Skip to content
Snippets Groups Projects
labsync-tray.py 26.9 KiB
Newer Older
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
# -*- coding: utf-8 -*-

Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed

import signal
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
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"
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
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

# not used in this version
#CURR_DIR = os.path.dirname(os.path.abspath(__file__))
# 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
# state names
APP_OFF = 'Off'
APP_SYNC = "Syncing"
APP_SYNCED = "Synced"
    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's avatar
Alberto LIVIO BECCARIA committed

# seconds between checks
CHECK_INTERVAL = 30
CHECK_INTERVAL_UNIT = 's' # seconds, according to previous value
# how long the notification stays on the screen (milliseconds; 0=until user clicks)
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
NOTIFY_TIME = 0
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
# messages
MSG_SYNC_TITLE = "labsync"
MSG_SYNC_TEXT = """Stiamo aggiornando il software nella cartella /opt; ci \
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
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&apos;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"
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
# 0 = normal, 1 = warning, 2 = critical
MSG_SYNCED_TITLE = 'labsync'
MSG_SYNCED_TEXT = "Il software nella cartella /opt è aggiornato."
MSG_SYNCED_ICON="dialog-information"
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
# 0 = normal, 1 = warning, 2 = critical
# file to check for saying if we are syncing or not
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
FILE_TO_CHECK = "/opt/ver"

STR_STATUS = "Stato"
STR_ABOUT = "Info"
STR_EXIT = "Esci"
STR_CLOSE = "Chiudi"
"""
--------------------------------------------------------------------------------
    Icons
--------------------------------------------------------------------------------
"""
    PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTkyIDE5MiIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCI+PGcgdHJhbnNmb3JtPSIiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTk2LDE5MmMtNTMuMDE5MzQsMCAtOTYsLTQyLjk4MDY2IC05NiwtOTZ2MGMwLC01My4wMTkzNCA0Mi45ODA2NiwtOTYgOTYsLTk2djBjNTMuMDE5MzQsMCA5Niw0Mi45ODA2NiA5Niw5NnYwYzAsNTMuMDE5MzQgLTQyLjk4MDY2LDk2IC05Niw5NnoiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSJNOTYsMTg4LjE2Yy01MC44OTg1NiwwIC05Mi4xNiwtNDEuMjYxNDQgLTkyLjE2LC05Mi4xNnYwYzAsLTUwLjg5ODU2IDQxLjI2MTQ0LC05Mi4xNiA5Mi4xNiwtOTIuMTZ2MGM1MC44OTg1NiwwIDkyLjE2LDQxLjI2MTQ0IDkyLjE2LDkyLjE2djBjMCw1MC44OTg1NiAtNDEuMjYxNDQsOTIuMTYgLTkyLjE2LDkyLjE2eiIgZmlsbD0ibm9uZSIvPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTMuODQsMTg4LjE2di0xODQuMzJoMTg0LjMydjE4NC4zMnoiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiLz48ZyBpZD0iTGF5ZXJfMSI+PGc+PGc+PGcgZmlsbD0iIzk1YTVhNiI+PGc+PHBhdGggZD0iTTIuOTYyNSw5NmMwLC01MS4zNzUgNDEuNjYyNSwtOTMuMDM3NSA5My4wMzc1LC05My4wMzc1YzUxLjM3NSwwIDkzLjAzNzUsNDEuNjYyNSA5My4wMzc1LDkzLjAzNzVjMCw1MS4zNzUgLTQxLjY2MjUsOTMuMDM3NSAtOTMuMDM3NSw5My4wMzc1Yy01MS4zNzUsMCAtOTMuMDM3NSwtNDEuNjYyNSAtOTMuMDM3NSwtOTMuMDM3NXoiLz48L2c+PC9nPjxwYXRoIGQ9Ik0xNjMuNTc1LDY3LjUzNzVjLTMuODYyNSwwIC03Ljc2MjUsMC4wMzc1IC0xMS42MjUsMC4wMzc1Yy04LjczNzUsLTE5LjU3NSAtMjUuODc1LC0zMy43MTI1IC00Ny40NzUsLTM3LjA4NzVjLTI0LjE1LC0zLjc4NzUgLTQ3LjIxMjUsNy4zODc1IC02MC45NzUsMjcuMTEyNWMtNi4xMTI1LDguNzM3NSA4LjI1LDE2Ljk1IDE0LjMyNSw4LjI1YzE4Ljg2MjUsLTI3IDU5LjE3NSwtMjQuNTYyNSA3NS43MTI1LDEuNzYyNWMtMy42LDAgLTcuMTYyNSwwIC0xMC43NjI1LDAuMDM3NWMtMS4wMTI1LDAgLTEuNzI1LDAuNDUgLTIuMTM3NSwxLjA4NzVjLTAuNzEyNSwwLjc4NzUgLTEuMDEyNSwxLjkxMjUgLTAuMjI1LDMuMDc1YzYuODI1LDEwLjU3NSAxMy42NSwyMS4xODc1IDIwLjQ3NSwzMS43NjI1YzEuMTYyNSwxLjc2MjUgMy42Mzc1LDEuNzYyNSA0Ljc2MjUsMGM2Ljc4NzUsLTEwLjYxMjUgMTMuNTM3NSwtMjEuMjYyNSAyMC4zMjUsLTMxLjg3NWMxLjA4NzUsLTEuOCAtMC40MTI1LC00LjE2MjUgLTIuNCwtNC4xNjI1eiIgZmlsbD0iI2ZmZmZmZiIvPjwvZz48cGF0aCBkPSJNMTM0LjM2MjUsMTI1LjY2MjVjLTE4LjcxMjUsMjYuODEyNSAtNTguNTM3NSwyNC42IC03NS4zMzc1LC0xLjEyNWMzLjQ1LDAgNi44NjI1LDAgMTAuMzEyNSwtMC4wMzc1YzEuMDEyNSwwIDEuNzI1LC0wLjQ1IDIuMTM3NSwtMS4wODc1YzAuNzEyNSwtMC43ODc1IDEuMDEyNSwtMS45MTI1IDAuMjI1LC0zLjA3NWMtNi44MjUsLTEwLjU3NSAtMTMuNjUsLTIxLjE4NzUgLTIwLjQ3NSwtMzEuNzYyNWMtMS4xNjI1LC0xLjggLTMuNjM3NSwtMS43NjI1IC00Ljc2MjUsMGMtNi43ODc1LDEwLjYxMjUgLTEzLjUzNzUsMjEuMjYyNSAtMjAuMzI1LDMxLjg3NWMtMS4xMjUsMS43NjI1IDAuMzM3NSw0LjEyNSAyLjM2MjUsNC4xMjVjNC4wMTI1LDAgNy45ODc1LC0wLjAzNzUgMTIsLTAuMDM3NWM4LjgxMjUsMTkuMjM3NSAyNS44LDMzLjExMjUgNDcuMjEyNSwzNi40ODc1YzI0LjExMjUsMy43ODc1IDQ3LjE3NSwtNy40MjUgNjAuOTM3NSwtMjcuMTEyNWM2LjExMjUsLTguNzM3NSAtOC4yNSwtMTYuOTEyNSAtMTQuMjg3NSwtOC4yNXoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PC9nPjxnPjxnIGlkPSJMYXllcl8xIj48cGF0aCBkPSJNMTg4LjgsMTUyLjU1NjhjMCwxOS44NzggLTE2LjEyMiwzNiAtMzYsMzZjLTE5Ljg3OCwwIC0zNiwtMTYuMTIyIC0zNiwtMzZjMCwtMTkuODc4IDE2LjEyMiwtMzYgMzYsLTM2YzE5Ljg3OCwwIDM2LDE2LjEyMiAzNiwzNiIgZmlsbD0iIzQwNDA0MSIvPjxwYXRoIGQ9Ik0xODIuOCwxNTIuNTU2OGMwLDE2LjU2NiAtMTMuNDM0LDMwIC0zMCwzMGMtMTYuNTY2LDAgLTMwLC0xMy40MzQgLTMwLC0zMGMwLC0xNi41NjYgMTMuNDM0LC0zMCAzMCwtMzBjMTYuNTY2LDAgMzAsMTMuNDM0IDMwLDMwIiBmaWxsPSIjZTg0ODQ5Ii8+PHBhdGggZD0iTTE0MC44LDE2Ny41NTY4Yy0wLjc2OCwwIC0xLjUzNiwtMC4yOTQgLTIuMTIxLC0wLjg3OWMtMS4xNzMsLTEuMTczIC0xLjE3MywtMy4wNjkgMCwtNC4yNDJsMjQsLTI0YzEuMTczLC0xLjE3MyAzLjA2OSwtMS4xNzMgNC4yNDIsMGMxLjE3MywxLjE3MyAxLjE3MywzLjA2OSAwLDQuMjQybC0yNCwyNGMtMC41ODUsMC41ODUgLTEuMzUzLDAuODc5IC0yLjEyMSwwLjg3OXoiIGZpbGw9IiNmZmZmZmYiLz48cGF0aCBkPSJNMTY0LjgsMTY3LjU1NjhjLTAuNzY4LDAgLTEuNTM2LC0wLjI5NCAtMi4xMjEsLTAuODc5bC0yNCwtMjRjLTEuMTczLC0xLjE3MyAtMS4xNzMsLTMuMDY5IDAsLTQuMjQyYzEuMTczLC0xLjE3MyAzLjA2OSwtMS4xNzMgNC4yNDIsMGwyNCwyNGMxLjE3MywxLjE3MyAxLjE3MywzLjA2OSAwLDQuMjQyYy0wLjU4NSwwLjU4NSAtMS4zNTMsMC44NzkgLTIuMTIxLDAuODc5eiIgZmlsbD0iI2ZmZmZmZiIvPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4K""",
    PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTkyIDE5MiIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCI+PGcgdHJhbnNmb3JtPSIiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTk2LDE5MmMtNTMuMDE5MzQsMCAtOTYsLTQyLjk4MDY2IC05NiwtOTZ2MGMwLC01My4wMTkzNCA0Mi45ODA2NiwtOTYgOTYsLTk2djBjNTMuMDE5MzQsMCA5Niw0Mi45ODA2NiA5Niw5NnYwYzAsNTMuMDE5MzQgLTQyLjk4MDY2LDk2IC05Niw5NnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9Ik05NiwxODguMTZjLTUwLjg5ODU2LDAgLTkyLjE2LC00MS4yNjE0NCAtOTIuMTYsLTkyLjE2djBjMCwtNTAuODk4NTYgNDEuMjYxNDQsLTkyLjE2IDkyLjE2LC05Mi4xNnYwYzUwLjg5ODU2LDAgOTIuMTYsNDEuMjYxNDQgOTIuMTYsOTIuMTZ2MGMwLDUwLjg5ODU2IC00MS4yNjE0NCw5Mi4xNiAtOTIuMTYsOTIuMTZ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSJNMCwxOTJ2LTE5MmgxOTJ2MTkyeiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTMuODQsMTg4LjE2di0xODQuMzJoMTg0LjMydjE4NC4zMnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxnIGlkPSJMYXllcl8xIiBzdHJva2U9Im5vbmUiPjxnPjxnPjxnIGZpbGw9IiMzNDk4ZGIiPjxnPjxwYXRoIGQ9Ik0yLjk2MjUsOTZjMCwtNTEuMzc1IDQxLjY2MjUsLTkzLjAzNzUgOTMuMDM3NSwtOTMuMDM3NWM1MS4zNzUsMCA5My4wMzc1LDQxLjY2MjUgOTMuMDM3NSw5My4wMzc1YzAsNTEuMzc1IC00MS42NjI1LDkzLjAzNzUgLTkzLjAzNzUsOTMuMDM3NWMtNTEuMzc1LDAgLTkzLjAzNzUsLTQxLjY2MjUgLTkzLjAzNzUsLTkzLjAzNzV6Ii8+PC9nPjwvZz48cGF0aCBkPSJNMTYzLjU3NSw2Ny41Mzc1Yy0zLjg2MjUsMCAtNy43NjI1LDAuMDM3NSAtMTEuNjI1LDAuMDM3NWMtOC43Mzc1LC0xOS41NzUgLTI1Ljg3NSwtMzMuNzEyNSAtNDcuNDc1LC0zNy4wODc1Yy0yNC4xNSwtMy43ODc1IC00Ny4yMTI1LDcuMzg3NSAtNjAuOTc1LDI3LjExMjVjLTYuMTEyNSw4LjczNzUgOC4yNSwxNi45NSAxNC4zMjUsOC4yNWMxOC44NjI1LC0yNyA1OS4xNzUsLTI0LjU2MjUgNzUuNzEyNSwxLjc2MjVjLTMuNiwwIC03LjE2MjUsMCAtMTAuNzYyNSwwLjAzNzVjLTEuMDEyNSwwIC0xLjcyNSwwLjQ1IC0yLjEzNzUsMS4wODc1Yy0wLjcxMjUsMC43ODc1IC0xLjAxMjUsMS45MTI1IC0wLjIyNSwzLjA3NWM2LjgyNSwxMC41NzUgMTMuNjUsMjEuMTg3NSAyMC40NzUsMzEuNzYyNWMxLjE2MjUsMS43NjI1IDMuNjM3NSwxLjc2MjUgNC43NjI1LDBjNi43ODc1LC0xMC42MTI1IDEzLjUzNzUsLTIxLjI2MjUgMjAuMzI1LC0zMS44NzVjMS4wODc1LC0xLjggLTAuNDEyNSwtNC4xNjI1IC0yLjQsLTQuMTYyNXoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PHBhdGggZD0iTTEzNC4zNjI1LDEyNS42NjI1Yy0xOC43MTI1LDI2LjgxMjUgLTU4LjUzNzUsMjQuNiAtNzUuMzM3NSwtMS4xMjVjMy40NSwwIDYuODYyNSwwIDEwLjMxMjUsLTAuMDM3NWMxLjAxMjUsMCAxLjcyNSwtMC40NSAyLjEzNzUsLTEuMDg3NWMwLjcxMjUsLTAuNzg3NSAxLjAxMjUsLTEuOTEyNSAwLjIyNSwtMy4wNzVjLTYuODI1LC0xMC41NzUgLTEzLjY1LC0yMS4xODc1IC0yMC40NzUsLTMxLjc2MjVjLTEuMTYyNSwtMS44IC0zLjYzNzUsLTEuNzYyNSAtNC43NjI1LDBjLTYuNzg3NSwxMC42MTI1IC0xMy41Mzc1LDIxLjI2MjUgLTIwLjMyNSwzMS44NzVjLTEuMTI1LDEuNzYyNSAwLjMzNzUsNC4xMjUgMi4zNjI1LDQuMTI1YzQuMDEyNSwwIDcuOTg3NSwtMC4wMzc1IDEyLC0wLjAzNzVjOC44MTI1LDE5LjIzNzUgMjUuOCwzMy4xMTI1IDQ3LjIxMjUsMzYuNDg3NWMyNC4xMTI1LDMuNzg3NSA0Ny4xNzUsLTcuNDI1IDYwLjkzNzUsLTI3LjExMjVjNi4xMTI1LC04LjczNzUgLTguMjUsLTE2LjkxMjUgLTE0LjI4NzUsLTguMjV6IiBmaWxsPSIjZmZmZmZmIi8+PC9nPjwvZz48ZyBzdHJva2U9Im5vbmUiPjxnIGlkPSJMYXllcl8xIj48cGF0aCBkPSJNMTg4LjgsMTUyLjU1NjhjMCwxOS44NzggLTE2LjEyMiwzNiAtMzYsMzZjLTE5Ljg3OCwwIC0zNiwtMTYuMTIyIC0zNiwtMzZjMCwtMTkuODc4IDE2LjEyMiwtMzYgMzYsLTM2YzE5Ljg3OCwwIDM2LDE2LjEyMiAzNiwzNiIgZmlsbD0iIzQwNDA0MSIvPjxwYXRoIGQ9Ik0xODIuOCwxNTIuNTU2OGMwLDE2LjU2NiAtMTMuNDM0LDMwIC0zMCwzMGMtMTYuNTY2LDAgLTMwLC0xMy40MzQgLTMwLC0zMGMwLC0xNi41NjYgMTMuNDM0LC0zMCAzMCwtMzBjMTYuNTY2LDAgMzAsMTMuNDM0IDMwLDMwIiBmaWxsPSIjMWZhODViIi8+PHBhdGggZD0iTTE0OS44LDE2Ny41NTY4Yy0wLjc2NzYyLDAgLTEuNTM1MjUsLTAuMjkyODcgLTIuMTIxLC0wLjg3OWwtMTIsLTEyYy0xLjE3MTg3LC0xLjE3MTg3IC0xLjE3MTg3LC0zLjA3MDUgMCwtNC4yNDIzOGMxLjE3MTg4LC0xLjE3MTg3IDMuMDcwNSwtMS4xNzE4NyA0LjI0MjM3LDBsOS44Nzg2Myw5Ljg3OTM4bDE4Ljg3OSwtMTguODc5YzEuMTcxODgsLTEuMTcxODcgMy4wNzA1LC0xLjE3MTg3IDQuMjQyMzcsMGMxLjE3MTg4LDEuMTcxODggMS4xNzE4OCwzLjA3MDUgMCw0LjI0MjM3bC0yMSwyMWMtMC41ODYxMiwwLjU4NTc1IC0xLjM1Mzc1LDAuODc4NjIgLTIuMTIxMzgsMC44Nzg2MnoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PC9nPjxwYXRoIGQ9Ik0xMTYuOCwxODguNTU2OHYtNzJoNzJ2NzJ6IiBpZD0ib3ZlcmxheS1kcmFnIiBmaWxsPSIjZmYwMDAwIiBzdHJva2U9Im5vbmUiIG9wYWNpdHk9IjAiLz48L2c+PC9nPjwvc3ZnPgo=""",
    PD94bWwgdmVyc2lvbj0iMS4wIj8+CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTkyIDE5MiIgd2lkdGg9IjY0cHgiIGhlaWdodD0iNjRweCI+PGcgdHJhbnNmb3JtPSIiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTk2LDE5MmMtNTMuMDE5MzQsMCAtOTYsLTQyLjk4MDY2IC05NiwtOTZ2MGMwLC01My4wMTkzNCA0Mi45ODA2NiwtOTYgOTYsLTk2djBjNTMuMDE5MzQsMCA5Niw0Mi45ODA2NiA5Niw5NnYwYzAsNTMuMDE5MzQgLTQyLjk4MDY2LDk2IC05Niw5NnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9Ik05NiwxODguMTZjLTUwLjg5ODU2LDAgLTkyLjE2LC00MS4yNjE0NCAtOTIuMTYsLTkyLjE2djBjMCwtNTAuODk4NTYgNDEuMjYxNDQsLTkyLjE2IDkyLjE2LC05Mi4xNnYwYzUwLjg5ODU2LDAgOTIuMTYsNDEuMjYxNDQgOTIuMTYsOTIuMTZ2MGMwLDUwLjg5ODU2IC00MS4yNjE0NCw5Mi4xNiAtOTIuMTYsOTIuMTZ6IiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSJNMCwxOTJ2LTE5MmgxOTJ2MTkyeiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iTTMuODQsMTg4LjE2di0xODQuMzJoMTg0LjMydjE4NC4zMnoiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxwYXRoIGQ9IiIgZmlsbD0ibm9uZSIgc3Ryb2tlPSJub25lIi8+PHBhdGggZD0iIiBmaWxsPSJub25lIiBzdHJva2U9Im5vbmUiLz48cGF0aCBkPSIiIGZpbGw9Im5vbmUiIHN0cm9rZT0ibm9uZSIvPjxnIGlkPSJMYXllcl8xIiBzdHJva2U9Im5vbmUiPjxnPjxnPjxnIGZpbGw9IiNlNjdlMjIiPjxnPjxwYXRoIGQ9Ik0yLjk2MjUsOTZjMCwtNTEuMzc1IDQxLjY2MjUsLTkzLjAzNzUgOTMuMDM3NSwtOTMuMDM3NWM1MS4zNzUsMCA5My4wMzc1LDQxLjY2MjUgOTMuMDM3NSw5My4wMzc1YzAsNTEuMzc1IC00MS42NjI1LDkzLjAzNzUgLTkzLjAzNzUsOTMuMDM3NWMtNTEuMzc1LDAgLTkzLjAzNzUsLTQxLjY2MjUgLTkzLjAzNzUsLTkzLjAzNzV6Ii8+PC9nPjwvZz48cGF0aCBkPSJNMTYzLjU3NSw2Ny41Mzc1Yy0zLjg2MjUsMCAtNy43NjI1LDAuMDM3NSAtMTEuNjI1LDAuMDM3NWMtOC43Mzc1LC0xOS41NzUgLTI1Ljg3NSwtMzMuNzEyNSAtNDcuNDc1LC0zNy4wODc1Yy0yNC4xNSwtMy43ODc1IC00Ny4yMTI1LDcuMzg3NSAtNjAuOTc1LDI3LjExMjVjLTYuMTEyNSw4LjczNzUgOC4yNSwxNi45NSAxNC4zMjUsOC4yNWMxOC44NjI1LC0yNyA1OS4xNzUsLTI0LjU2MjUgNzUuNzEyNSwxLjc2MjVjLTMuNiwwIC03LjE2MjUsMCAtMTAuNzYyNSwwLjAzNzVjLTEuMDEyNSwwIC0xLjcyNSwwLjQ1IC0yLjEzNzUsMS4wODc1Yy0wLjcxMjUsMC43ODc1IC0xLjAxMjUsMS45MTI1IC0wLjIyNSwzLjA3NWM2LjgyNSwxMC41NzUgMTMuNjUsMjEuMTg3NSAyMC40NzUsMzEuNzYyNWMxLjE2MjUsMS43NjI1IDMuNjM3NSwxLjc2MjUgNC43NjI1LDBjNi43ODc1LC0xMC42MTI1IDEzLjUzNzUsLTIxLjI2MjUgMjAuMzI1LC0zMS44NzVjMS4wODc1LC0xLjggLTAuNDEyNSwtNC4xNjI1IC0yLjQsLTQuMTYyNXoiIGZpbGw9IiNmZmZmZmYiLz48L2c+PHBhdGggZD0iTTEzNC4zNjI1LDEyNS42NjI1Yy0xOC43MTI1LDI2LjgxMjUgLTU4LjUzNzUsMjQuNiAtNzUuMzM3NSwtMS4xMjVjMy40NSwwIDYuODYyNSwwIDEwLjMxMjUsLTAuMDM3NWMxLjAxMjUsMCAxLjcyNSwtMC40NSAyLjEzNzUsLTEuMDg3NWMwLjcxMjUsLTAuNzg3NSAxLjAxMjUsLTEuOTEyNSAwLjIyNSwtMy4wNzVjLTYuODI1LC0xMC41NzUgLTEzLjY1LC0yMS4xODc1IC0yMC40NzUsLTMxLjc2MjVjLTEuMTYyNSwtMS44IC0zLjYzNzUsLTEuNzYyNSAtNC43NjI1LDBjLTYuNzg3NSwxMC42MTI1IC0xMy41Mzc1LDIxLjI2MjUgLTIwLjMyNSwzMS44NzVjLTEuMTI1LDEuNzYyNSAwLjMzNzUsNC4xMjUgMi4zNjI1LDQuMTI1YzQuMDEyNSwwIDcuOTg3NSwtMC4wMzc1IDEyLC0wLjAzNzVjOC44MTI1LDE5LjIzNzUgMjUuOCwzMy4xMTI1IDQ3LjIxMjUsMzYuNDg3NWMyNC4xMTI1LDMuNzg3NSA0Ny4xNzUsLTcuNDI1IDYwLjkzNzUsLTI3LjExMjVjNi4xMTI1LC04LjczNzUgLTguMjUsLTE2LjkxMjUgLTE0LjI4NzUsLTguMjV6IiBmaWxsPSIjZmZmZmZmIi8+PC9nPjwvZz48ZyBzdHJva2U9Im5vbmUiPjxnIGlkPSJMYXllcl8xIj48cGF0aCBkPSJNMTg4LjgsMTUyLjU1NjhjMCwxOS44NzggLTE2LjEyMiwzNiAtMzYsMzZjLTE5Ljg3OCwwIC0zNiwtMTYuMTIyIC0zNiwtMzZjMCwtMTkuODc4IDE2LjEyMiwtMzYgMzYsLTM2YzE5Ljg3OCwwIDM2LDE2LjEyMiAzNiwzNiIgZmlsbD0iIzQwNDA0MSIvPjxwYXRoIGQ9Ik0xNTIuOCwxMjIuNTU2OGMxNi41NjYsMCAzMCwxMy40MzQgMzAsMzBjMCwxNi41NjYgLTEzLjQzNCwzMCAtMzAsMzBjLTE2LjU2NiwwIC0zMCwtMTMuNDM0IC0zMCwtMzBjMCwtMTYuNTY2IDEzLjQzNCwtMzAgMzAsLTMwIiBmaWxsPSIjZmZmZmZmIi8+PHBhdGggZD0iTTE2NC44LDE2Ny41NTY4Yy0wLjc2OCwwIC0xLjUzNiwtMC4yOTQgLTIuMTIxLC0wLjg3OWwtMTIsLTEyYy0wLjU2NCwtMC41NjEgLTAuODc5LC0xLjMyNiAtMC44NzksLTIuMTIxdi0yMWMwLC0xLjY1NiAxLjM0NCwtMyAzLC0zYzEuNjU2LDAgMywxLjM0NCAzLDN2MTkuNzU4bDExLjEyMSwxMS4xMjFjMS4xNzMsMS4xNzMgMS4xNzMsMy4wNjkgMCw0LjI0MmMtMC41ODUsMC41ODUgLTEuMzUzLDAuODc5IC0yLjEyMSwwLjg3OXoiIGZpbGw9IiMyNmE5ZTAiLz48Y2lyY2xlIGN4PSI0MDcuNDY2NjciIGN5PSI0MDYuODE4MTMiIHRyYW5zZm9ybT0ic2NhbGUoMC4zNzUsMC4zNzUpIiByPSIxMiIgZmlsbD0iIzI2YTllMCIvPjwvZz48L2c+PHBhdGggZD0iTTExNi44LDE4OC41NTY4di03Mmg3MnY3MnoiIGlkPSJvdmVybGF5LWRyYWciIGZpbGw9IiNmZjAwMDAiIHN0cm9rZT0ibm9uZSIgb3BhY2l0eT0iMCIvPjwvZz48L2c+PC9zdmc+Cg=="""
}
"""
--------------------------------------------------------------------------------
    Main application class
--------------------------------------------------------------------------------
"""
class TrayApp():
    def __init__(self, appid, icon=None, menu=None, check_interval=None):
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        self.task_changed = True
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        self.current_notification = None
        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's avatar
Alberto LIVIO BECCARIA committed
        if menu != None:
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        self.menu.show_all()
        if APPINDICATOR_SUPPORT == 1 and FORCE_TRAY == False:
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
            self.tray = AppIndicator3.Indicator.new(
                appid, self.icon, AppIndicator3.IndicatorCategory.APPLICATION_STATUS)
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
            self.tray.set_status(AppIndicator3.IndicatorStatus.ACTIVE)
            self.tray.set_menu(self.menu)
        else:
            self.tray = Gtk.StatusIcon()
            self.tray.set_visible(True);
            self.tray.connect('popup-menu', self.on_popup_menu)
            self.tray.connect("button-press-event", self.on_mouse_click)
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
            self.tray.set_tooltip_text(APP_NAME)
            self.tray.set_title(APP_NAME)
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        # initialize task value
        self.update_task()
        # intial icon could be different from the current state, force update
        self.update_icon()
        # send notification on start only if labsync is syncing
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        if self.task_changed:
            self.send_notification()
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        # http://developer.gnome.org/pygobject/stable/glib-functions.html#function-glib--timeout-add-seconds
        GLib.timeout_add_seconds(self.check_interval, self.handler_timeout)
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        # 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's avatar
Alberto LIVIO BECCARIA committed
        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)
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        item_status.connect("activate", self.handler_menu_status)
        menu.append(item_status)
        about = Gtk.MenuItem(label=STR_ABOUT)
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        about.connect("activate", self.handler_menu_about)
        menu.append(about)
        menu.append(Gtk.SeparatorMenuItem())
        item_quit = Gtk.MenuItem(label=STR_EXIT)
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        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
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        else:
            title = MSG_SYNCED_TITLE
            txt = MSG_SYNCED_TEXT
            icon = MSG_SYNCED_ICON
            urgency = MSG_SYNCED_URGENCY
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        # close the current notification, if any
        if self.current_notification != None:
            self.current_notification.close()
            self.current_notification = None
        self.current_notification = Message(None, title, txt, icon, NOTIFY_TIME, urgency, self.show_about_dialog).notification
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        self.current_notification.connect("closed", self.handler_current_notification_closed)

    def handler_current_notification_closed(self, evt):
        self.current_notification = None

Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        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()

    def handler_menu_about(self, evt):
        self.show_about_dialog()

        if APPINDICATOR_SUPPORT == 1 and FORCE_TRAY == False:
            self.tray.set_icon_full(self.icon, "-")
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        else:
            self.tray.set_from_file(self.icon, "-")
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed

    def update_task(self):
        exists = os.path.isfile(FILE_TO_CHECK)
        if exists:
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        else:
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        if self.task != task:
            self.task_changed = True
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        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
--------------------------------------------------------------------------------
"""
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
class Message():
    def mess_callback(*args, **kwargs):
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
        pass

    def __init__(self, parent, caption, msg, img=None, timeout=None,
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed

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

    # 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)
        # 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():
        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):
            os.remove(value)

# detect AppIndicator support
APPINDICATOR_SUPPORT = 1
try:
    from gi.repository import AppIndicator3
except:
    APPINDICATOR_SUPPORT = 0
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
if __name__ == "__main__":
    # parse command line arguments
    parser = argparse.ArgumentParser(description=APP_NAME + ' ' + APP_VERSION)
    parser.add_argument('-i', '--interval', type=int,
        help='check interval (seconds)')
Alberto LIVIO BECCARIA's avatar
Alberto LIVIO BECCARIA committed
    args = parser.parse_args()

    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's avatar
Alberto LIVIO BECCARIA committed

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