diff --git a/.i3/lemonbar/i3_lemonbar_common.py b/.i3/lemonbar/i3_lemonbar_common.py index 6a4c5bf..b481994 100755 --- a/.i3/lemonbar/i3_lemonbar_common.py +++ b/.i3/lemonbar/i3_lemonbar_common.py @@ -6,6 +6,10 @@ import i3_lemonbar_config as config p_screen = None kill_on_unfocus = [] +# Loggers, initialized in main function +logger = None +health_logger = None + class bar_mode(Enum): power, normal, control = range(-1,2) # Don't cycle through power diff --git a/.i3/lemonbar/i3_lemonbar_launcher.py b/.i3/lemonbar/i3_lemonbar_launcher.py index 9967028..f72a523 100755 --- a/.i3/lemonbar/i3_lemonbar_launcher.py +++ b/.i3/lemonbar/i3_lemonbar_launcher.py @@ -35,37 +35,37 @@ def assert_only_instance(): with open(config.pid_file, 'r') as fp: pid = int(fp.read()) except IOError: - logger.debug('Could not open PID file. Assuming non existent') + common.logger.debug('Could not open PID file. Assuming non existent') except ValueError: - logger.debug('''PID file contents broken''') + common.logger.debug('''PID file contents broken''') try: os.remove(config.pid_file) - logger.debug('''Deleted old PID file, continuing as usual''') + common.logger.debug('''Deleted old PID file, continuing as usual''') except OSError: - logger.debug('''Failed deleting old PID file.''') + common.logger.debug('''Failed deleting old PID file.''') os._exit(1) if pid is not None: try: - logger.debug('''Found old PID file. Looking for owner''') + common.logger.debug('''Found old PID file. Looking for owner''') os.kill(pid, 0) - logger.debug('''Owner exists''') - logger.debug('''Failed, another instance of the launcher is running + common.logger.debug('''Owner exists''') + common.logger.debug('''Failed, another instance of the launcher is running (PID {})'''.format(pid)) os._exit(1) except ProcessLookupError: - logger.debug('''Owner does not exist''') + common.logger.debug('''Owner does not exist''') try: os.remove(config.pid_file) - logger.debug('''Deleted old PID file, continuing as usual''') + common.logger.debug('''Deleted old PID file, continuing as usual''') except OSError: - logger.debug('''Failed deleting old PID file.''') + common.logger.debug('''Failed deleting old PID file.''') os._exit(1) with open(config.pid_file, 'w+') as fp: fp.write('{:d}'.format(os.getpid())) - logger.debug('''Created and wrote to PID file''') + common.logger.debug('''Created and wrote to PID file''') create_new_fifo(config.fifo_file_status) create_new_fifo(config.fifo_file_executor) @@ -76,22 +76,22 @@ def create_new_fifo(fifo_file): """ try: os.remove(fifo_file) - logger.debug('''Removed old fifo file''') + common.logger.debug('''Removed old fifo file''') except OSError: - logger.debug('''No old fifo file, good''') + common.logger.debug('''No old fifo file, good''') try: os.mkfifo(fifo_file) except OSError: - logger.error('''Failed, couldn't create fifo file''') + common.logger.error('''Failed, couldn't create fifo file''') os.remove(config.pid_file) # Clean up own PID file sys.exit(0) def handle_exit(signum, frame): - logger.info('Signal handler called with signal {}'.format(signum)) - logger.info('Calling os._exit(0)') + common.logger.info('Signal handler called with signal {}'.format(signum)) + common.logger.info('Calling os._exit(0)') os._exit(0) # Terminates process p nicely @@ -104,7 +104,7 @@ def nice_delete(f): os.remove(f) def clean_up(): - logger.debug('Cleaning up') + common.logger.debug('Cleaning up') nice_delete(config.pid_file) nice_delete(config.fifo_file_status) nice_delete(config.fifo_file_executor) @@ -120,11 +120,12 @@ def write_sys_status(): with open(config.fifo_file_status, 'w') as fifo: p_conky_slow = subprocess.Popen(['conky', '-c', config.path+'conky_slow'], # Use communicate stdout=fifo, stderr=fifo) - logger.debug('Started conky slow') + common.logger.debug('Started conky slow') p_conky_fast = subprocess.Popen(['conky', '-c', config.path+'conky_fast'], stdout=fifo, stderr=fifo) - logger.debug('Started conky fast') + common.logger.debug('Started conky fast') i3_ws_obj = wspaces.i3ws(fifo_file=config.fifo_file_status) + i3_ws_obj.work() def parse_status(): global p_lemonbar @@ -133,13 +134,13 @@ def parse_status(): ,stdin=subprocess.PIPE, stdout=fifo_write, text=True) with open(config.fifo_file_status, 'r', buffering=1) as fifo_read: # Let parser read from fifo - logger.debug("FIFO {} opened for reading".format(config.fifo_file_status)) + common.logger.debug("FIFO {} opened for reading".format(config.fifo_file_status)) lemonparser.parse_line('WSPINA1___main INA2___web FOC5___terms INA6___stats ') # TODO modular while True: try: data = fifo_read.readline() # Blocking read if len(data) == 0: - logger.debug("Writer closed") + common.logger.debug("Writer closed") break psd = lemonparser.parse_line(data) p_lemonbar.stdin.write(lemonparser.parse_line(data) + '\n') @@ -147,21 +148,21 @@ def parse_status(): #logger.debug('Read: "{0}"'.format(data)) #logger.debug('Parsed "{0}"'.format(psd)) except BrokenPipeError: - logger.debug('Broken pipe in parse status thread, exiting') - health_logger.info('Broken pipe in parse status thread, exiting') + common.logger.debug('Broken pipe in parse status thread, exiting') + common.health_logger.info('Broken pipe in parse status thread, exiting') clean_up() except: - logger.debug('Unknown exception in parse status thread, exiting') - health_logger.info('Unknown exception in parse status thread, exiting') + common.logger.debug('Unknown exception in parse status thread, exiting') + common.health_logger.info('Unknown exception in parse status thread, exiting') clean_up() def exec_commands(): with open(config.fifo_file_executor, 'r', buffering=1) as fifo_read: - logger.debug("FIFO {} opened for reading".format(config.fifo_file_executor)) + common.logger.debug("FIFO {} opened for reading".format(config.fifo_file_executor)) while True: try: data = fifo_read.readline() - logger.debug('Trying reading: "{0}"'.format(data.strip('\n'))) + common.logger.debug('Trying reading: "{0}"'.format(data.strip('\n'))) try: for key,func in common.commands_dict.items(): l = len(key) @@ -170,17 +171,17 @@ def exec_commands(): break except: - logger.debug('Exception occured executing command\n Line in: {}\n Data: {}'.format(line_in, line_in[l:].split())) + common.logger.debug('Exception occured executing command\n Line in: {}\n Data: {}'.format(line_in, line_in[l:].split())) if len(data) == 0: - logger.debug("Lemonbar output closed") + common.logger.debug("Lemonbar output closed") break - logger.debug('Read: "{0}"'.format(data.strip('\n'))) + common.logger.debug('Read: "{0}"'.format(data.strip('\n'))) except BrokenPipeError: - logger.debug('Broken pipe in exec commands thread, exiting') - health_logger.info('Broken pipe in exec commands thread, exiting') + common.logger.debug('Broken pipe in exec commands thread, exiting') + common.health_logger.info('Broken pipe in exec commands thread, exiting') clean_up() except: - health_logger.info('Unknown exception in exec commands thread, exiting') + common.health_logger.info('Unknown exception in exec commands thread, exiting') raise clean_up() def strip_ansi_unicode(s): @@ -196,7 +197,7 @@ def user_screen(): ,stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True) for line in common.p_screen.stdout: #try: - logger.debug('Screen read line {}'.format(line.strip('\n'))) + common.logger.debug('Screen read line {}'.format(line.strip('\n'))) # with open(config.fifo_file_status, 'w') as fifo: # clean_line = strip_ansi_unicode(line) # if (not clean_line.startswith('[bluetooth]') @@ -215,13 +216,13 @@ class i3_thread: self.desc = desc self.all_threads.append(self) - health_logger.info('"%s" starting', desc) + common.health_logger.info('"%s" starting', desc) self.thread.start() # Wrapper around the target def run(target, desc): target() - health_logger.info('"%s" reached end', desc) + common.health_logger.info('"%s" reached end', desc) def join_threads(): for i3_th in i3_thread.all_threads: @@ -241,19 +242,19 @@ if __name__ == "__main__": handler = logging.StreamHandler(sys.stdout) handler.setFormatter(formatter) - logger = logging.getLogger('Normal logger') - logger.setLevel(debuglvl) - logger.addHandler(handler) + common.logger = logging.getLogger('Normal logger') + common.logger.setLevel(debuglvl) + common.logger.addHandler(handler) # Setup health logger to file in tmp formatter = logging.Formatter('%(asctime)s %(message)s') handler = logging.FileHandler(config.health_file) handler.setFormatter(formatter) - health_logger = logging.getLogger('Health logger') - health_logger.addHandler(handler) + common.health_logger = logging.getLogger('Health logger') + common.health_logger.addHandler(handler) -# logger.basicConfig(stream=sys.stdout, level=debuglvl) +# common.logger.basicConfig(stream=sys.stdout, level=debuglvl) assert_only_instance() # Creates pid file and fifo file atexit.register(clean_up) signal.signal(signal.SIGTERM, handle_exit) @@ -266,7 +267,7 @@ if __name__ == "__main__": i3_thread(target = write_sys_status, desc='Write sys status thread') i3_thread(target = user_screen, desc='Screen thread for user commands') - logger.debug('Threads started') + common.logger.debug('Threads started') i3_thread.join_threads() - logger.debug('Reached end') + common.logger.debug('Reached end') diff --git a/.i3/lemonbar/i3_workspaces.py b/.i3/lemonbar/i3_workspaces.py index 5cd1838..1c5a505 100755 --- a/.i3/lemonbar/i3_workspaces.py +++ b/.i3/lemonbar/i3_workspaces.py @@ -56,11 +56,15 @@ class State(object): return self.inactive +# Create the i3ws object, then call locking function work() +# Another implementation would be to create a thread in __init__ +# and implement a locking join() function class i3ws(object): - ws_format = '%s%s ' - end_format = 'WSP%s' - state = State() + ws_format = '%s%s ' + end_format = 'WSP%s' + state = State() backgrounds = {} + running = True fifo = None @@ -69,25 +73,40 @@ class i3ws(object): self.state = state if fifo_file: self.fifo = open(fifo_file, 'w') + + def work(self): + # While loop to restart connection as long as there is an i3 ipc socket + while self.running: + self.resetConn() + common.logger.debug('Started i3 workspaces manager') + self.enterMain() + time.sleep(0.5) # TODO max number of attempts + catch exception instead + + def resetConn(self): # conn self.conn = i3ipc.Connection() - # Run call backs once self.change(self.conn, None) self.win_focused(self.conn, None) - self.conn.on('workspace::focus', self.change) - self.conn.on('workspace::init', self.change) - self.conn.on('workspace::empty', self.change) - self.conn.on('window::focus', self.win_focused) - logging.debug('Started i3 workspaces manager') + self.conn.on('workspace::focus' , self.change) + self.conn.on('workspace::init' , self.change) + self.conn.on('workspace::empty' , self.change) + self.conn.on('window::focus' , self.win_focused) + self.conn.on('shutdown' , self.shutdown) + + def enterMain(self): try: + # Locking call self.conn.main() except BrokenPipeError: - logging.debug('Broken pipe in i3 workspaces thread, exiting') + common.logger.debug('Broken pipe in i3 workspaces thread, exiting') except: - logging.debug('Unknown exception in i3 workspaces thread, exiting') + common.logger.debug('Unknown exception in i3 workspaces thread, exiting') + + def shutdown(self, i3, e): + common.logger.debug('Shut down i3ipc') def change(self, i3, e): # Receives event and workspace data @@ -121,16 +140,16 @@ class i3ws(object): try: os.kill(pid, signal.SIGTERM) except ProcessLookupError: - logging.debug('Tried killing process {} but it doesn\'t exist'.format(pid)) + common.logger.debug('Tried killing process {} but it doesn\'t exist'.format(pid)) common.kill_on_unfocus = [] # Set correct keymap if wclass in common.keymaps: new_km = common.keymaps[wclass] - logging.debug('Setting {} as keymap for {}'.format(new_km, wclass)) + common.logger.debug('Setting {} as keymap for {}'.format(new_km, wclass)) else: new_km = common.def_keymap - logging.debug('Setting default keymap {} for {}'.format(common.def_keymap, wclass)) + common.logger.debug('Setting default keymap {} for {}'.format(common.def_keymap, wclass)) call(['/home/kuba/.i3/scripts/lang.sh', 'qset', new_km]) @@ -169,18 +188,34 @@ class i3ws(object): call(['sh', '/home/kuba/.i3/lemonbar/set_bg.sh', args]) def quit(self): + common.logger.debug('Quitting i3 workspace script') + self.running = False + self.conn.main_quit() if self.fifo is not None: try: self.fifo.close() except BrokenPipeError: pass +def handle_exit(signal, frame): + global ws + print("Recieved Keyboard Interrupt from user") + ws.quit() + sys.exit(1) + if __name__ == '__main__': + # Setup logger to stdout with debug log level + formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s') + handler = logging.StreamHandler(sys.stdout) + handler.setFormatter(formatter) + + common.logger = logging.getLogger('Normal logger') + common.logger.setLevel(logging.DEBUG) + common.logger.addHandler(handler) + + # Capture Keyboard Interrupt (i3ipc captures this internally) + signal.signal(signal.SIGINT, handle_exit) + + # Start main ws = i3ws() - try: - while True: - time.sleep(1) - except KeyboardInterrupt: - print('') # force new line -# finally: -# ws.quit() + ws.work()