From 4aba84ced0ea1a7a2c26941b8faeb86e246d8896 Mon Sep 17 00:00:00 2001 From: kuben Date: Sun, 11 Aug 2019 20:12:04 +0200 Subject: [PATCH] lemonbar: Screen working somewhat. Remove one fifo --- .i3/lemonbar/i3_lemonbar_common.py | 59 +++++++++-- .i3/lemonbar/i3_lemonbar_config.py | 1 - .i3/lemonbar/i3_lemonbar_launcher.py | 152 ++++++++++++++------------- .i3/lemonbar/i3_lemonbar_parser.py | 6 +- .i3/scripts/lang.sh | 6 +- .xinitrc | 2 +- 6 files changed, 137 insertions(+), 89 deletions(-) diff --git a/.i3/lemonbar/i3_lemonbar_common.py b/.i3/lemonbar/i3_lemonbar_common.py index bb64fa7..4a13dde 100755 --- a/.i3/lemonbar/i3_lemonbar_common.py +++ b/.i3/lemonbar/i3_lemonbar_common.py @@ -1,4 +1,4 @@ -import subprocess +import subprocess, os, getpass from enum import Enum import i3_lemonbar_config as config @@ -9,6 +9,9 @@ kill_on_unfocus = [] logger = None health_logger = None +screen = None +parsing_queue = None + class bar_mode(Enum): power, normal, control = range(-1,2) # Don't cycle through power @@ -20,9 +23,6 @@ class bar_mode(Enum): mode = bar_mode.normal -screen_args = ['screen', '-d', '-m', '-U', '-S', 'lemonbar' - , '-L', '-Logfile', config.fifo_screen_log] - floatterm_args = lambda prog : ['terminator', '-r', 'FLOAT_TERM', '-p' , 'dark', '-e', 'echo kill_unfocus $$ > {}; exec {}'.format( config.fifo_file_executor, prog)] @@ -66,16 +66,13 @@ def bluetooth(args): btcargs = args.split()[1:] btcargs = [a.replace('pxc550', '00:16:94:22:29:0E') for a in btcargs] inp = ' '.join(btcargs) - stuff_screen(inp) + screen.send_cmd('bluetoothctl ' + inp) show_secs = False def toggle_secs(args): global show_secs show_secs = not show_secs -def stuff_screen(cmd): - subprocess.run(['screen', '-S', 'lemonbar', '-X', 'stuff', "echo " + cmd + "\\n"]) - # Keymaps def_keymap = 'pl' keymaps = {'Firefox': 'se'} @@ -154,3 +151,49 @@ def set_keymap(i3ws): new_km = def_keymap logger.debug('Setting default keymap {} for {}'.format(def_keymap, wclass)) subprocess.call(['/home/kuba/.i3/scripts/lang.sh', 'qset', new_km]) + +def create_new_fifo(fifo_file): + """ + Create new fifo file, removing old one if it exists + """ + try: + os.remove(fifo_file) + logger.debug('''Removed old fifo file''') + except OSError: + logger.debug('''No old fifo file, good''') + + try: + os.mkfifo(fifo_file) + except OSError: + logger.error('''Failed, couldn't create fifo file''') + os.remove(config.pid_file) # Clean up own PID file + sys.exit(0) + +class LemonbarScreen: + # Start, stop and send commands to screen instance + + # Start detached, in UTF-8 mode. Log to fifo + start_flags = ['-d', '-m', '-U', '-L', '-Logfile', config.fifo_screen_log] + + def __init__(self): + self.identifier = 'lemonbar_{}_{}'.format(getpass.getuser(), os.getpid()) + + create_new_fifo(config.fifo_screen_log) + self.send(self.start_flags) # Start screen + self.send_cmd('stty -echo') # Do not echo what is written to screen terminal + self.send_colon('logfile flush 0.1') # Log quickly + + def destroy(self): + self.send_colon('kill') + + def send(self, args): + # Send something to our screen instance + subprocess.call(['screen', '-S', self.identifier] + args) + + def send_cmd(self, cmd): + # Send terminal input to our screen instance + self.send(['-X', 'stuff', cmd + '\n']) + + def send_colon(self, cmd): + # Send colon command to our screen instance + self.send(['-X', 'colon', cmd + '\n']) diff --git a/.i3/lemonbar/i3_lemonbar_config.py b/.i3/lemonbar/i3_lemonbar_config.py index 865ae43..9c289a7 100755 --- a/.i3/lemonbar/i3_lemonbar_config.py +++ b/.i3/lemonbar/i3_lemonbar_config.py @@ -4,7 +4,6 @@ import getpass # File locations pid_file = '/tmp/i3_lemonbar_launcher.pid' fifo_file_status = '/tmp/i3_lemonbar1_{}'.format(getpass.getuser()) -fifo_file_executor = '/tmp/i3_lemonbar2_{}'.format(getpass.getuser()) fifo_screen_log = '/tmp/i3_screen_{}'.format(getpass.getuser()) health_file = '/tmp/i3_lemonbar_health.info' diff --git a/.i3/lemonbar/i3_lemonbar_launcher.py b/.i3/lemonbar/i3_lemonbar_launcher.py index 16f80c5..8bf1f44 100755 --- a/.i3/lemonbar/i3_lemonbar_launcher.py +++ b/.i3/lemonbar/i3_lemonbar_launcher.py @@ -1,4 +1,5 @@ import fcntl, sys, os, time, logging +import queue import signal, atexit import subprocess import contextlib @@ -67,27 +68,7 @@ def assert_only_instance(): fp.write('{:d}'.format(os.getpid())) common.logger.debug('''Created and wrote to PID file''') - create_new_fifo(config.fifo_file_status) - create_new_fifo(config.fifo_file_executor) - -def create_new_fifo(fifo_file): - """ - Create new fifo file, removing old one if it exists - """ - try: - os.remove(fifo_file) - common.logger.debug('''Removed old fifo file''') - except OSError: - common.logger.debug('''No old fifo file, good''') - - try: - os.mkfifo(fifo_file) - except OSError: - common.logger.error('''Failed, couldn't create fifo file''') - os.remove(config.pid_file) # Clean up own PID file - sys.exit(0) - - + common.create_new_fifo(config.fifo_file_status) def handle_exit(signum, frame): common.logger.info('Signal handler called with signal {}'.format(signum)) @@ -105,19 +86,19 @@ def nice_delete(f): def clean_up(): common.logger.debug('Cleaning up') + common.screen.destroy() nice_delete(config.pid_file) nice_delete(config.fifo_file_status) - nice_delete(config.fifo_file_executor) nice_term(p_conky_slow) nice_term(p_conky_fast) - common.stuff_screen('kill') if i3_ws_obj is not None: i3_ws_obj.quit() sys.exit(0) def write_sys_status(): global p_conky_slow, p_conky_fast, i3_ws_obj - with open(config.fifo_file_status, 'w') as fifo: + # Buffering = 1 to write entire lines + with open(config.fifo_file_status, 'w', buffering=1) as fifo: p_conky_slow = subprocess.Popen(['conky', '-c', config.path+'conky_slow'], # Use communicate stdout=fifo, stderr=fifo) common.logger.debug('Started conky slow') @@ -127,26 +108,21 @@ def write_sys_status(): i3_ws_obj = wspaces.i3ws(fifo_file=config.fifo_file_status) i3_ws_obj.work() -def parse_status(): - global p_lemonbar - with open(config.fifo_file_executor, 'w') as fifo_write: - p_lemonbar = subprocess.Popen(config.lemonbar_args - ,stdin=subprocess.PIPE, stdout=fifo_write, text=True) +def queue_parse_job(job): + common.parsing_queue.put(job) + +def put_fifo_in_queue(): with open(config.fifo_file_status, 'r', buffering=1) as fifo_read: # Let parser read from fifo 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: common.logger.debug("Writer closed") break - psd = lemonparser.parse_line(data) - p_lemonbar.stdin.write(lemonparser.parse_line(data) + '\n') - p_lemonbar.stdin.flush() - #logger.debug('Read: "{0}"'.format(data)) - #logger.debug('Parsed "{0}"'.format(psd)) + queue_parse_job(data) except BrokenPipeError: common.logger.debug('Broken pipe in parse status thread, exiting') common.health_logger.info('Broken pipe in parse status thread, exiting') @@ -156,61 +132,86 @@ def parse_status(): 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: - common.logger.debug("FIFO {} opened for reading".format(config.fifo_file_executor)) - while True: - try: - data = fifo_read.readline() - common.logger.debug('Trying reading: "{0}"'.format(data.strip('\n'))) - try: - for key,func in common.commands_dict.items(): - l = len(key) - if data[:l] == key: - func(data) - break +def parse_status(): + global p_lemonbar + p_lemonbar = subprocess.Popen(config.lemonbar_args, stdin=subprocess.PIPE + , stdout=subprocess.PIPE, text=True) - except: - common.logger.debug('Exception occured executing command\n Line in: {}\n Data: {}'.format(line_in, line_in[l:].split())) - if len(data) == 0: - common.logger.debug("Lemonbar output closed") + lemonparser.parse_line('WSPINA1___main INA2___web FOC5___terms INA6___stats ') # TODO modular + while True: + data = common.parsing_queue.get() # Blocking read + if data is None: + common.logger.debug('Queue closed') + common.health_logger.info('Queue closed') + break + + psd = lemonparser.parse_line(data) + p_lemonbar.stdin.write(psd + '\n') + p_lemonbar.stdin.flush() + #common.logger.debug('Read: "{0}"'.format(data)) + #common.logger.debug('Parsed "{0}"'.format(psd)) + +def exec_commands(): + global p_lemonbar + + # Wait until up TODO better solution + while True: + if p_lemonbar is not None: + break + while True: + data = p_lemonbar.stdout.readline() + if not data: + common.logger.debug('Lemonbar closed, exiting') + common.health_logger.info('Lemonbar closed, exiting') + clean_up() + break + + common.logger.debug('Trying reading: "{0}"'.format(data.strip('\n'))) + try: + for key,func in common.commands_dict.items(): + l = len(key) + if data[:l] == key: + func(data) break - common.logger.debug('Read: "{0}"'.format(data.strip('\n'))) - except BrokenPipeError: - common.logger.debug('Broken pipe in exec commands thread, exiting') - common.health_logger.info('Broken pipe in exec commands thread, exiting') - clean_up() - except: - common.health_logger.info('Unknown exception in exec commands thread, exiting') - raise - clean_up() + + except: + common.logger.debug('Exception occured executing command\n Line in: {}\n Data: {}'.format(line_in, line_in[l:].split())) + if len(data) == 0: + common.logger.debug("Lemonbar output closed") + break + common.logger.debug('Read: "{0}"'.format(data.strip('\n'))) + +ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]') def strip_ansi_unicode(s): # ANSI escape sequences are for colors in terminal and similar - ansi_escape = re.compile(r'(\x9B|\x1B\[)[0-?]*[ -/]*[@-~]') strip_ansi = ansi_escape.sub('', s) strip_unicode = (strip_ansi.encode('ascii', 'ignore')).decode('utf-8') return strip_unicode def user_screen(): # Create screen session, in UTF-8 mode, logging to fifo - create_new_fifo(config.fifo_screen_log) - subprocess.Popen(common.screen_args) # TODO lemonbar_user + common.screen = common.LemonbarScreen() with open(config.fifo_screen_log, 'r', buffering=1) as screen_read: - while True: + empty_count = 0 + while empty_count < 3: try: - line = screen_read.readline() + line = screen_read.readline().strip('\n') + line = strip_ansi_unicode(line) + common.logger.debug('Screen read line {}'.format(line)) + if (not line.startswith('[CHG]') + and not line.isspace()): + #common.logger.debug('put in queue {}'.format(line)) + queue_parse_job('RESP {}\n'.format(line)) + if not line: - break - 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]') - # and not clean_line.startswith('[CHG]') - # and not clean_line.isspace()): - # fifo.write('RESP [bluetoothctl] ' + clean_line) + # End loop if many empty lines in a row + empty_count = empty_count + 1 + else: + empty_count = 0 except: raise + common.screen.destroy() nice_delete(config.fifo_screen_log) class i3_thread: @@ -266,10 +267,13 @@ if __name__ == "__main__": signal.signal(signal.SIGTERM, handle_exit) signal.signal(signal.SIGINT, handle_exit) + common.parsing_queue = queue.Queue() + # Start writing and reading threads # Create readers before writers i3_thread(target = exec_commands, desc='Exec commands thread') i3_thread(target = parse_status, desc='Parse status thread') + i3_thread(target = put_fifo_in_queue, desc='') i3_thread(target = write_sys_status, desc='Write sys status thread') i3_thread(target = user_screen, desc='Screen thread for user commands') diff --git a/.i3/lemonbar/i3_lemonbar_parser.py b/.i3/lemonbar/i3_lemonbar_parser.py index e9c58cf..116a6a0 100755 --- a/.i3/lemonbar/i3_lemonbar_parser.py +++ b/.i3/lemonbar/i3_lemonbar_parser.py @@ -99,7 +99,9 @@ controls = ' '.join([block(fg=conf.color_head, bg=conf.color_sec_b2) ]) def update_response(data): global response + resp = ' '.join(data) + #common.logger.debug('Got response {}'.format(resp)) response= ''.join(['%{r}', single_sect(text=resp)]) def update_displays(data): @@ -282,8 +284,8 @@ def parse_line(line_in): print('Exception occured\n Line in: {}\n Data: {}'.format(line_in, line_in[l:].split())) raise - #return '%s\n' + format_line() # Done this way in .sh - return format_line() + formatted_line = format_line() + return formatted_line if __name__ == "__main__": for line in sys.stdin: diff --git a/.i3/scripts/lang.sh b/.i3/scripts/lang.sh index df0147d..b3b6740 100755 --- a/.i3/scripts/lang.sh +++ b/.i3/scripts/lang.sh @@ -3,7 +3,7 @@ panel_fifo="/tmp/i3_lemonbar1_${USER}" panel_commands="/tmp/i3_lemonbar2_${USER}" function show(){ - setxkbmap -print -display :0 | grep xkb_symbols | awk '{print $4}' | awk -F"+" '{print $2}' + setxkbmap -print | grep xkb_symbols | awk '{print $4}' | awk -F"+" '{print $2}' } if [ $# -lt 1 ]; then echo LANG$(show) @@ -14,7 +14,7 @@ if [ "$1" == "next" ]; then if [ "$cur" == "se" ]; then next='pl' fi - setxkbmap -display :0 $next + setxkbmap $next if [ -e $panel_fifo ]; then echo -e "LANG$next\n" > "${panel_fifo}" fi @@ -23,7 +23,7 @@ if [ "$1" == "next" ]; then fi elif [ "$1" == "qset" ]; then next=$2 - setxkbmap -display :0 $next + setxkbmap $next if [ -e $panel_fifo ]; then echo -e "LANG$next\n" > "${panel_fifo}" fi diff --git a/.xinitrc b/.xinitrc index 05dca24..439abec 100755 --- a/.xinitrc +++ b/.xinitrc @@ -43,7 +43,7 @@ if [ "$WM" = "i3" ]; then elif [ "`hostname`" = "JakubArch" ] && [ -f ~/.i3/work.config ]; then cat ~/.i3/work.config >> $i3config fi - exec i3 -c $i3config >> ~/${WM}.log 2>&1 + exec i3 -c $i3config -V >> ~/${WM}.log 2>&1 else exec $WM >> "~/${WM}.log" 2>&1 fi