aboutsummaryrefslogtreecommitdiff
path: root/src/usr/bin/logout-manager-ncurses
diff options
context:
space:
mode:
Diffstat (limited to 'src/usr/bin/logout-manager-ncurses')
-rwxr-xr-xsrc/usr/bin/logout-manager-ncurses193
1 files changed, 193 insertions, 0 deletions
diff --git a/src/usr/bin/logout-manager-ncurses b/src/usr/bin/logout-manager-ncurses
new file mode 100755
index 0000000..7ff5e18
--- /dev/null
+++ b/src/usr/bin/logout-manager-ncurses
@@ -0,0 +1,193 @@
+#!/usr/bin/env python3
+# File: logout-manager-ncurses
+# License: MIT
+# Author: adamlamers, bgstack15
+# Startdate: 2020-03-09 17:06
+# Title: ncurses based logout manager
+# Usage:
+# logout-manager-ncurses
+# Reference:
+# https://docs.python.org/3/howto/curses.html
+# ripped straight from http://adamlamers.com/post/FTPD9KNRA8CT
+# https://stackoverflow.com/questions/3061/calling-a-function-of-a-module-by-using-its-name-a-string/12025554#12025554
+# https://robinislam.me/blog/reading-environment-variables-in-python/
+# Improve:
+# Dependencies:
+# dep-devuan: python3-dotenv
+# Documentation:
+# Improvements for CursesMenu class over origin:
+# accepts number key inputs
+# accepts enabled attribute
+# add "zeroindex" bool
+
+import curses, os, platform, sys
+from dotenv import load_dotenv
+
+# all this to load the libpath
+try:
+ defaultdir="/etc/sysconfig"
+ thisplatform = platform.platform().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)
+import lmlib
+
+class CursesMenu(object):
+
+ INIT = {'type' : 'init'}
+
+ def __init__(self, menu_options):
+ self.screen = curses.initscr()
+ self.menu_options = menu_options
+ self.selected_option = 0
+ self._previously_selected_option = None
+ self.running = True
+ self._zero_offset = 1
+ try:
+ self._zero_offset = 0 if bool(self.menu_options['zeroindex']) else 1
+ except:
+ pass
+
+ #init curses and curses input
+ curses.noecho()
+ curses.cbreak()
+ curses.start_color()
+ curses.curs_set(0) #Hide cursor
+ self.screen.keypad(1)
+
+ #set up color pair for highlighted option
+ curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE)
+ self.hilite_color = curses.color_pair(1)
+ self.normal_color = curses.A_NORMAL
+
+ def prompt_selection(self, parent=None):
+ if parent is None:
+ lastoption = "Cancel"
+ else:
+ lastoption = "Return to previous menu ({})".format(parent['title'])
+
+ option_count = len(self.menu_options['options'])
+
+ input_key = None
+
+ ENTER_KEY = ord('\n')
+ NUM_KEYS = [ord(str(i)) for i in range(self._zero_offset,option_count+1+self._zero_offset)]
+ done = False
+ while not done:
+ if self.selected_option != self._previously_selected_option:
+ self._previously_selected_option = self.selected_option
+
+ self.screen.border(0)
+ self._draw_title()
+ for option in range(option_count):
+ if self.selected_option == option:
+ self._draw_option(option, self.hilite_color)
+ else:
+ self._draw_option(option, self.normal_color)
+
+ if self.selected_option == option_count:
+ self.screen.addstr(4 + option_count, 4, "{:2} - {}".format(option_count+self._zero_offset,
+ lastoption), self.hilite_color)
+ else:
+ self.screen.addstr(4 + option_count, 4, "{:2} - {}".format(option_count+self._zero_offset,
+ lastoption), self.normal_color)
+
+ max_y, max_x = self.screen.getmaxyx()
+ if input_key is not None:
+ self.screen.addstr(max_y-3, max_x - 5, "{:3}".format(self.selected_option+self._zero_offset))
+ self.screen.refresh()
+
+
+ input_key = self.screen.getch()
+ down_keys = [curses.KEY_DOWN, ord('j')]
+ up_keys = [curses.KEY_UP, ord('k')]
+ exit_keys = [ord('q')]
+
+ if input_key in down_keys:
+ if self.selected_option < option_count:
+ self.selected_option += 1
+ else:
+ self.selected_option = 0
+
+ if input_key in up_keys:
+ if self.selected_option > 0:
+ self.selected_option -= 1
+ else:
+ self.selected_option = option_count
+
+ if input_key in exit_keys:
+ self.selected_option = option_count #auto select exit and return
+ break
+
+ if input_key == ENTER_KEY or input_key in NUM_KEYS:
+ if input_key in NUM_KEYS:
+ self.selected_option=int(chr(input_key))-self._zero_offset
+ done = True
+ try:
+ done = self.menu_options['options'][self.selected_option]['enabled']
+ except:
+ pass
+ return self.selected_option
+
+ def _draw_option(self, option_number, style):
+ thistext = self.menu_options['options'][option_number]['title']
+ try:
+ if self.menu_options['options'][option_number]['enabled'] == False: thistext += " (disabled)"
+ except:
+ pass
+ self.screen.addstr(4 + option_number,
+ 4,
+ "{:2} - {}".format(option_number+self._zero_offset, thistext),
+ style)
+
+ def _draw_title(self):
+ self.screen.addstr(2, 2, self.menu_options['title'], curses.A_STANDOUT)
+ self.screen.addstr(3, 2, self.menu_options['subtitle'], curses.A_BOLD)
+
+ def display(self):
+ selected_option = self.prompt_selection()
+ i, _ = self.screen.getmaxyx()
+ curses.endwin()
+ #os.system('clear')
+ if selected_option < len(self.menu_options['options']):
+ selected_opt = self.menu_options['options'][selected_option]
+ return selected_opt
+ else:
+ self.running = False
+ return {'title' : 'Cancel', 'type' : 'exitmenu'}
+
+# load configs
+config = lmlib.Initialize_config(os.environ['LOGOUT_MANAGER_CONF'])
+actions = lmlib.Actions
+
+# MAIN LOOP
+menu = {
+ 'title' : 'Logout Manager',
+ 'type' : 'menu',
+ 'subtitle' : 'Use arrows or number keys',
+ 'zeroindex' : False,
+ 'options' : [
+ {'title': 'Lock', 'type': 'action', 'action': 'lock'},
+ {'title': 'Logout', 'type': 'action', 'action': 'logout'},
+ {'title': 'Hibernate', 'type': 'action', 'action': 'hibernate', 'enabled': config.can_hibernate},
+ {'title': 'Shutdown', 'type': 'action', 'action': 'shutdown'},
+ {'title': 'Reboot', 'type': 'action', 'action': 'reboot'}
+ ]
+}
+m = CursesMenu(menu)
+selected_action = m.display()
+
+if selected_action['type'] == 'exitmenu':
+ print("Cancel any logout action.")
+elif selected_action['type'] == 'command':
+ os.system(selected_action['command'])
+elif selected_action['type'] == 'action':
+ #a = selected_action['action']:
+ func = getattr(globals()['actions'],selected_action['action'])
+ func(config)
bgstack15