#!/usr/bin/env python3
# File: logout-manager-trayicon
# License: CC-BY-SA 4.0
# Author: bgstack15
# Startdate: 2020-03-20
# Title: Logout Manager tray icon
# Purpose: An easy menu from the system tray in a panel for a window manager or desktop environment
# History:
#    2020-04-01 update for python 3.8
# Reference:
#    icon work https://stackoverflow.com/questions/45162862/how-do-i-set-an-icon-for-the-whole-application-using-pygobject
#    button right click must be from "button-press-event" and import Gdk https://python-gtk-3-tutorial.readthedocs.io/en/latest/menus.html
#    useful reference https://lazka.github.io/pgi-docs/Gtk-3.0/classes/Button.html#Gtk.Button
#    systray info https://github.com/PiSupply/PiJuice/blob/master/Software/Source/src/pijuice_tray.py
#    logout-manager-gtk
#    how to determine double click https://stackoverflow.com/questions/60009648/is-there-a-better-way-to-handle-double-click-in-pygobject
#    interactive python3 shell and help(Gdk.EventType)
#    https://developer.gnome.org/gtk3/unstable/GtkWidget.html#GtkWidget-button-press-event
#    find running processes https://thispointer.com/python-get-list-of-all-running-processes-and-sort-by-highest-memory-usage/
#    send signals https://stackoverflow.com/questions/15080500/how-can-i-send-a-signal-from-a-python-program
#    https://docs.python.org/3.8/library/signal.html#module-signal
# Dependencies:
#    dep-pip: psutil, distro
#    dep-devuan: python3-psutil, python3-distro

import gi, os, re, sys, psutil, signal
from distro import linux_distribution
gi.require_version("Gtk","3.0")
from gi.repository import Gtk
from gi.repository import Gdk
from dotenv import load_dotenv

# all this to load the libpath
try:
   defaultdir="/etc/sysconfig"
   thisplatform = linux_distribution()[0].lower()
   if 'debian' in thisplatform or 'devuan' in thisplatform:
      defaultdir="/etc/default"
   # load_dotenv keeps existing environment variables as higher precedent
   load_dotenv(os.path.join(defaultdir,"logout-manager"))
except:
   pass
if 'LOGOUT_MANAGER_LIBPATH' in os.environ:
   for i in os.environ['LOGOUT_MANAGER_LIBPATH'].split(":"):
      sys.path.append(i)
sys.path.append("/usr/share/logout-manager")
import lmlib

def is_dryrun():
   result = False
   try:
      if "DRYRUN" in os.environ and os.environ["DRYRUN"] != "": result = True
   except:
      pass
   return result

def run_or_kill_logout_manager():
   #print("Run or kill logout manager!")
   _lm_is_running = False
   lmregex = re.compile("logout-manager.*--from-trayicon")
   lmprintregex = re.compile("logout-manager")
   thisproc = None
   for proc in psutil.process_iter():
      try:
         cmdline = " ".join(proc.cmdline())
         #if lmprintregex.search(cmdline) != None: print("Checking \"" + cmdline + "\"")
         if lmregex.search(cmdline) != None:
            _lm_is_running = True
            thisproc = proc
            break
      except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
         pass

   if _lm_is_running:
      #print("Stopping the following process.")
      #print(thisproc)
      os.kill(thisproc.pid,signal.SIGHUP)
   else:
      # start new instance
      #print("Please start a new instance")

      # this actually returns the new pid, and we could choose to signal only this pid.
      # but we will not at this point.
      newpid = os.spawnvp(os.P_NOWAIT,"logout-manager",["logout-manager","--from-trayicon"])
      print("spawned",newpid)

class MainIcon(Gtk.StatusIcon):
   def __init__(self,config,actions):
      Gtk.StatusIcon.__init__(self)
      self.config = config
      self.actions = actions
      self.set_from_icon_name(self.config.get_logout_icon())
      loggedin_str = "Logged in as " + str(os.environ["USER"])
      tooltiptext = loggedin_str
      if is_dryrun():
         tooltiptext = "DRYRUN MODE: " + tooltiptext
      self.set_tooltip_text(tooltiptext)

      self.traymenu = Gtk.Menu()
      self.add_action_to_menu("Loc_k",self.config.get_lock_icon(),self.on_lock_menuitem)
      self.add_action_to_menu("_Logout",self.config.get_logout_icon(),self.on_logout_menuitem)
      self.add_action_to_menu("_Hibernate",self.config.get_hibernate_icon(),self.on_hibernate_menuitem)
      self.add_action_to_menu("_Shutdown",self.config.get_shutdown_icon(),self.on_shutdown_menuitem)
      self.add_action_to_menu("_Reboot",self.config.get_reboot_icon(),self.on_reboot_menuitem)

      # separator
      i = Gtk.SeparatorMenuItem.new()
      i.show()
      self.traymenu.append(i)

      # logged in as
      i = Gtk.MenuItem.new_with_label(tooltiptext)
      i.set_sensitive(False)
      i.show()
      self.traymenu.append(i)

      # hide tray icon
      i = Gtk.MenuItem.new_with_mnemonic("Hide _tray icon")
      i.show()
      i.connect("activate", self.exit)
      self.traymenu.append(i)

      self.connect("button-press-event", self.on_button_press_event)
      self.connect("popup-menu", self.show_menu)

   def on_button_press_event(self, b_unknown, event: Gdk.EventButton):
      if Gdk.EventType._2BUTTON_PRESS == event.type:
         run_or_kill_logout_manager()

   def exit(self, widget):
      quit()

   def show_menu(self, widget, event_button, event_time):
      self.traymenu.popup(None, None,
         self.position_menu,
         self,
         event_button,
         Gtk.get_current_event_time())

   def on_lock_menuitem(self, widget):
      self.actions.lock(self.config)

   def on_logout_menuitem(self, widget):
      self.actions.logout(self.config)

   def on_hibernate_menuitem(self, widget):
      self.actions.hibernate(self.config)

   def on_shutdown_menuitem(self, widget):
      self.actions.shutdown(self.config)

   def on_reboot_menuitem(self, widget):
      self.actions.reboot(self.config)

   def add_action_to_menu(self,label_str,icon_str,function_func):
      i = Gtk.ImageMenuItem.new_with_mnemonic(label_str)
      j = Gtk.Image.new_from_icon_name(icon_str,32)
      j.show()
      i.set_image(j)
      i.set_always_show_image(True)
      i.show()
      i.connect("activate", function_func)
      self.traymenu.append(i)

# load configs
config = lmlib.Initialize_config(os.environ['LOGOUT_MANAGER_CONF'])
actions = lmlib.Actions

icon = MainIcon(config,actions)
Gtk.main()
