lemonbar: Handle restarts
This commit is contained in:
@@ -6,6 +6,10 @@ import i3_lemonbar_config as config
|
|||||||
p_screen = None
|
p_screen = None
|
||||||
kill_on_unfocus = []
|
kill_on_unfocus = []
|
||||||
|
|
||||||
|
# Loggers, initialized in main function
|
||||||
|
logger = None
|
||||||
|
health_logger = None
|
||||||
|
|
||||||
class bar_mode(Enum):
|
class bar_mode(Enum):
|
||||||
power, normal, control = range(-1,2) # Don't cycle through power
|
power, normal, control = range(-1,2) # Don't cycle through power
|
||||||
|
|
||||||
|
|||||||
@@ -35,37 +35,37 @@ def assert_only_instance():
|
|||||||
with open(config.pid_file, 'r') as fp:
|
with open(config.pid_file, 'r') as fp:
|
||||||
pid = int(fp.read())
|
pid = int(fp.read())
|
||||||
except IOError:
|
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:
|
except ValueError:
|
||||||
logger.debug('''PID file contents broken''')
|
common.logger.debug('''PID file contents broken''')
|
||||||
try:
|
try:
|
||||||
os.remove(config.pid_file)
|
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:
|
except OSError:
|
||||||
logger.debug('''Failed deleting old PID file.''')
|
common.logger.debug('''Failed deleting old PID file.''')
|
||||||
os._exit(1)
|
os._exit(1)
|
||||||
|
|
||||||
|
|
||||||
if pid is not None:
|
if pid is not None:
|
||||||
try:
|
try:
|
||||||
logger.debug('''Found old PID file. Looking for owner''')
|
common.logger.debug('''Found old PID file. Looking for owner''')
|
||||||
os.kill(pid, 0)
|
os.kill(pid, 0)
|
||||||
logger.debug('''Owner exists''')
|
common.logger.debug('''Owner exists''')
|
||||||
logger.debug('''Failed, another instance of the launcher is running
|
common.logger.debug('''Failed, another instance of the launcher is running
|
||||||
(PID {})'''.format(pid))
|
(PID {})'''.format(pid))
|
||||||
os._exit(1)
|
os._exit(1)
|
||||||
except ProcessLookupError:
|
except ProcessLookupError:
|
||||||
logger.debug('''Owner does not exist''')
|
common.logger.debug('''Owner does not exist''')
|
||||||
try:
|
try:
|
||||||
os.remove(config.pid_file)
|
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:
|
except OSError:
|
||||||
logger.debug('''Failed deleting old PID file.''')
|
common.logger.debug('''Failed deleting old PID file.''')
|
||||||
os._exit(1)
|
os._exit(1)
|
||||||
|
|
||||||
with open(config.pid_file, 'w+') as fp:
|
with open(config.pid_file, 'w+') as fp:
|
||||||
fp.write('{:d}'.format(os.getpid()))
|
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_status)
|
||||||
create_new_fifo(config.fifo_file_executor)
|
create_new_fifo(config.fifo_file_executor)
|
||||||
@@ -76,22 +76,22 @@ def create_new_fifo(fifo_file):
|
|||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
os.remove(fifo_file)
|
os.remove(fifo_file)
|
||||||
logger.debug('''Removed old fifo file''')
|
common.logger.debug('''Removed old fifo file''')
|
||||||
except OSError:
|
except OSError:
|
||||||
logger.debug('''No old fifo file, good''')
|
common.logger.debug('''No old fifo file, good''')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.mkfifo(fifo_file)
|
os.mkfifo(fifo_file)
|
||||||
except OSError:
|
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
|
os.remove(config.pid_file) # Clean up own PID file
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def handle_exit(signum, frame):
|
def handle_exit(signum, frame):
|
||||||
logger.info('Signal handler called with signal {}'.format(signum))
|
common.logger.info('Signal handler called with signal {}'.format(signum))
|
||||||
logger.info('Calling os._exit(0)')
|
common.logger.info('Calling os._exit(0)')
|
||||||
os._exit(0)
|
os._exit(0)
|
||||||
|
|
||||||
# Terminates process p nicely
|
# Terminates process p nicely
|
||||||
@@ -104,7 +104,7 @@ def nice_delete(f):
|
|||||||
os.remove(f)
|
os.remove(f)
|
||||||
|
|
||||||
def clean_up():
|
def clean_up():
|
||||||
logger.debug('Cleaning up')
|
common.logger.debug('Cleaning up')
|
||||||
nice_delete(config.pid_file)
|
nice_delete(config.pid_file)
|
||||||
nice_delete(config.fifo_file_status)
|
nice_delete(config.fifo_file_status)
|
||||||
nice_delete(config.fifo_file_executor)
|
nice_delete(config.fifo_file_executor)
|
||||||
@@ -120,11 +120,12 @@ def write_sys_status():
|
|||||||
with open(config.fifo_file_status, 'w') as fifo:
|
with open(config.fifo_file_status, 'w') as fifo:
|
||||||
p_conky_slow = subprocess.Popen(['conky', '-c', config.path+'conky_slow'], # Use communicate
|
p_conky_slow = subprocess.Popen(['conky', '-c', config.path+'conky_slow'], # Use communicate
|
||||||
stdout=fifo, stderr=fifo)
|
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'],
|
p_conky_fast = subprocess.Popen(['conky', '-c', config.path+'conky_fast'],
|
||||||
stdout=fifo, stderr=fifo)
|
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 = wspaces.i3ws(fifo_file=config.fifo_file_status)
|
||||||
|
i3_ws_obj.work()
|
||||||
|
|
||||||
def parse_status():
|
def parse_status():
|
||||||
global p_lemonbar
|
global p_lemonbar
|
||||||
@@ -133,13 +134,13 @@ def parse_status():
|
|||||||
,stdin=subprocess.PIPE, stdout=fifo_write, text=True)
|
,stdin=subprocess.PIPE, stdout=fifo_write, text=True)
|
||||||
with open(config.fifo_file_status, 'r', buffering=1) as fifo_read:
|
with open(config.fifo_file_status, 'r', buffering=1) as fifo_read:
|
||||||
# Let parser read from fifo
|
# 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
|
lemonparser.parse_line('WSPINA1___main INA2___web FOC5___terms INA6___stats ') # TODO modular
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
data = fifo_read.readline() # Blocking read
|
data = fifo_read.readline() # Blocking read
|
||||||
if len(data) == 0:
|
if len(data) == 0:
|
||||||
logger.debug("Writer closed")
|
common.logger.debug("Writer closed")
|
||||||
break
|
break
|
||||||
psd = lemonparser.parse_line(data)
|
psd = lemonparser.parse_line(data)
|
||||||
p_lemonbar.stdin.write(lemonparser.parse_line(data) + '\n')
|
p_lemonbar.stdin.write(lemonparser.parse_line(data) + '\n')
|
||||||
@@ -147,21 +148,21 @@ def parse_status():
|
|||||||
#logger.debug('Read: "{0}"'.format(data))
|
#logger.debug('Read: "{0}"'.format(data))
|
||||||
#logger.debug('Parsed "{0}"'.format(psd))
|
#logger.debug('Parsed "{0}"'.format(psd))
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
logger.debug('Broken pipe in parse status thread, exiting')
|
common.logger.debug('Broken pipe in parse status thread, exiting')
|
||||||
health_logger.info('Broken pipe in parse status thread, exiting')
|
common.health_logger.info('Broken pipe in parse status thread, exiting')
|
||||||
clean_up()
|
clean_up()
|
||||||
except:
|
except:
|
||||||
logger.debug('Unknown exception in parse status thread, exiting')
|
common.logger.debug('Unknown exception in parse status thread, exiting')
|
||||||
health_logger.info('Unknown exception in parse status thread, exiting')
|
common.health_logger.info('Unknown exception in parse status thread, exiting')
|
||||||
clean_up()
|
clean_up()
|
||||||
|
|
||||||
def exec_commands():
|
def exec_commands():
|
||||||
with open(config.fifo_file_executor, 'r', buffering=1) as fifo_read:
|
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:
|
while True:
|
||||||
try:
|
try:
|
||||||
data = fifo_read.readline()
|
data = fifo_read.readline()
|
||||||
logger.debug('Trying reading: "{0}"'.format(data.strip('\n')))
|
common.logger.debug('Trying reading: "{0}"'.format(data.strip('\n')))
|
||||||
try:
|
try:
|
||||||
for key,func in common.commands_dict.items():
|
for key,func in common.commands_dict.items():
|
||||||
l = len(key)
|
l = len(key)
|
||||||
@@ -170,17 +171,17 @@ def exec_commands():
|
|||||||
break
|
break
|
||||||
|
|
||||||
except:
|
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:
|
if len(data) == 0:
|
||||||
logger.debug("Lemonbar output closed")
|
common.logger.debug("Lemonbar output closed")
|
||||||
break
|
break
|
||||||
logger.debug('Read: "{0}"'.format(data.strip('\n')))
|
common.logger.debug('Read: "{0}"'.format(data.strip('\n')))
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
logger.debug('Broken pipe in exec commands thread, exiting')
|
common.logger.debug('Broken pipe in exec commands thread, exiting')
|
||||||
health_logger.info('Broken pipe in exec commands thread, exiting')
|
common.health_logger.info('Broken pipe in exec commands thread, exiting')
|
||||||
clean_up()
|
clean_up()
|
||||||
except:
|
except:
|
||||||
health_logger.info('Unknown exception in exec commands thread, exiting')
|
common.health_logger.info('Unknown exception in exec commands thread, exiting')
|
||||||
raise
|
raise
|
||||||
clean_up()
|
clean_up()
|
||||||
def strip_ansi_unicode(s):
|
def strip_ansi_unicode(s):
|
||||||
@@ -196,7 +197,7 @@ def user_screen():
|
|||||||
,stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
|
,stdin=subprocess.PIPE, stdout=subprocess.PIPE, text=True)
|
||||||
for line in common.p_screen.stdout:
|
for line in common.p_screen.stdout:
|
||||||
#try:
|
#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:
|
# with open(config.fifo_file_status, 'w') as fifo:
|
||||||
# clean_line = strip_ansi_unicode(line)
|
# clean_line = strip_ansi_unicode(line)
|
||||||
# if (not clean_line.startswith('[bluetooth]')
|
# if (not clean_line.startswith('[bluetooth]')
|
||||||
@@ -215,13 +216,13 @@ class i3_thread:
|
|||||||
self.desc = desc
|
self.desc = desc
|
||||||
|
|
||||||
self.all_threads.append(self)
|
self.all_threads.append(self)
|
||||||
health_logger.info('"%s" starting', desc)
|
common.health_logger.info('"%s" starting', desc)
|
||||||
self.thread.start()
|
self.thread.start()
|
||||||
|
|
||||||
# Wrapper around the target
|
# Wrapper around the target
|
||||||
def run(target, desc):
|
def run(target, desc):
|
||||||
target()
|
target()
|
||||||
health_logger.info('"%s" reached end', desc)
|
common.health_logger.info('"%s" reached end', desc)
|
||||||
|
|
||||||
def join_threads():
|
def join_threads():
|
||||||
for i3_th in i3_thread.all_threads:
|
for i3_th in i3_thread.all_threads:
|
||||||
@@ -241,19 +242,19 @@ if __name__ == "__main__":
|
|||||||
handler = logging.StreamHandler(sys.stdout)
|
handler = logging.StreamHandler(sys.stdout)
|
||||||
handler.setFormatter(formatter)
|
handler.setFormatter(formatter)
|
||||||
|
|
||||||
logger = logging.getLogger('Normal logger')
|
common.logger = logging.getLogger('Normal logger')
|
||||||
logger.setLevel(debuglvl)
|
common.logger.setLevel(debuglvl)
|
||||||
logger.addHandler(handler)
|
common.logger.addHandler(handler)
|
||||||
|
|
||||||
# Setup health logger to file in tmp
|
# Setup health logger to file in tmp
|
||||||
formatter = logging.Formatter('%(asctime)s %(message)s')
|
formatter = logging.Formatter('%(asctime)s %(message)s')
|
||||||
handler = logging.FileHandler(config.health_file)
|
handler = logging.FileHandler(config.health_file)
|
||||||
handler.setFormatter(formatter)
|
handler.setFormatter(formatter)
|
||||||
|
|
||||||
health_logger = logging.getLogger('Health logger')
|
common.health_logger = logging.getLogger('Health logger')
|
||||||
health_logger.addHandler(handler)
|
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
|
assert_only_instance() # Creates pid file and fifo file
|
||||||
atexit.register(clean_up)
|
atexit.register(clean_up)
|
||||||
signal.signal(signal.SIGTERM, handle_exit)
|
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 = write_sys_status, desc='Write sys status thread')
|
||||||
i3_thread(target = user_screen, desc='Screen thread for user commands')
|
i3_thread(target = user_screen, desc='Screen thread for user commands')
|
||||||
|
|
||||||
logger.debug('Threads started')
|
common.logger.debug('Threads started')
|
||||||
i3_thread.join_threads()
|
i3_thread.join_threads()
|
||||||
|
|
||||||
logger.debug('Reached end')
|
common.logger.debug('Reached end')
|
||||||
|
|||||||
@@ -56,11 +56,15 @@ class State(object):
|
|||||||
return self.inactive
|
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):
|
class i3ws(object):
|
||||||
ws_format = '%s%s '
|
ws_format = '%s%s '
|
||||||
end_format = 'WSP%s'
|
end_format = 'WSP%s'
|
||||||
state = State()
|
state = State()
|
||||||
backgrounds = {}
|
backgrounds = {}
|
||||||
|
running = True
|
||||||
|
|
||||||
fifo = None
|
fifo = None
|
||||||
|
|
||||||
@@ -69,25 +73,40 @@ class i3ws(object):
|
|||||||
self.state = state
|
self.state = state
|
||||||
if fifo_file:
|
if fifo_file:
|
||||||
self.fifo = open(fifo_file, 'w')
|
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
|
# conn
|
||||||
self.conn = i3ipc.Connection()
|
self.conn = i3ipc.Connection()
|
||||||
|
|
||||||
|
|
||||||
# Run call backs once
|
# Run call backs once
|
||||||
self.change(self.conn, None)
|
self.change(self.conn, None)
|
||||||
self.win_focused(self.conn, None)
|
self.win_focused(self.conn, None)
|
||||||
|
|
||||||
self.conn.on('workspace::focus', self.change)
|
self.conn.on('workspace::focus' , self.change)
|
||||||
self.conn.on('workspace::init', self.change)
|
self.conn.on('workspace::init' , self.change)
|
||||||
self.conn.on('workspace::empty', self.change)
|
self.conn.on('workspace::empty' , self.change)
|
||||||
self.conn.on('window::focus', self.win_focused)
|
self.conn.on('window::focus' , self.win_focused)
|
||||||
logging.debug('Started i3 workspaces manager')
|
self.conn.on('shutdown' , self.shutdown)
|
||||||
|
|
||||||
|
def enterMain(self):
|
||||||
try:
|
try:
|
||||||
|
# Locking call
|
||||||
self.conn.main()
|
self.conn.main()
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
logging.debug('Broken pipe in i3 workspaces thread, exiting')
|
common.logger.debug('Broken pipe in i3 workspaces thread, exiting')
|
||||||
except:
|
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):
|
def change(self, i3, e):
|
||||||
# Receives event and workspace data
|
# Receives event and workspace data
|
||||||
@@ -121,16 +140,16 @@ class i3ws(object):
|
|||||||
try:
|
try:
|
||||||
os.kill(pid, signal.SIGTERM)
|
os.kill(pid, signal.SIGTERM)
|
||||||
except ProcessLookupError:
|
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 = []
|
common.kill_on_unfocus = []
|
||||||
|
|
||||||
# Set correct keymap
|
# Set correct keymap
|
||||||
if wclass in common.keymaps:
|
if wclass in common.keymaps:
|
||||||
new_km = common.keymaps[wclass]
|
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:
|
else:
|
||||||
new_km = common.def_keymap
|
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])
|
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])
|
call(['sh', '/home/kuba/.i3/lemonbar/set_bg.sh', args])
|
||||||
|
|
||||||
def quit(self):
|
def quit(self):
|
||||||
|
common.logger.debug('Quitting i3 workspace script')
|
||||||
|
self.running = False
|
||||||
|
self.conn.main_quit()
|
||||||
if self.fifo is not None:
|
if self.fifo is not None:
|
||||||
try:
|
try:
|
||||||
self.fifo.close()
|
self.fifo.close()
|
||||||
except BrokenPipeError:
|
except BrokenPipeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def handle_exit(signal, frame):
|
||||||
|
global ws
|
||||||
|
print("Recieved Keyboard Interrupt from user")
|
||||||
|
ws.quit()
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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()
|
ws = i3ws()
|
||||||
try:
|
ws.work()
|
||||||
while True:
|
|
||||||
time.sleep(1)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print('') # force new line
|
|
||||||
# finally:
|
|
||||||
# ws.quit()
|
|
||||||
|
|||||||
Reference in New Issue
Block a user