import sys from enum import Enum import i3_lemonbar_config as conf import i3_lemonbar_common as common displays = '' workspaces = '' win_title = '' time = '' response = '' blank = ' ' sr = conf.sep_right slr = conf.sep_l_right sl = conf.sep_left sll = conf.sep_l_left class LemonUnit: def __init__(self, name, action = None, alt_scheme=None, external=None): self.name = name self.action = action self.alt_scheme = alt_scheme # None means default self.external = external self.items = [] # List of tuples (icon, text) class LemonParser: # Handle parsing of units # Apply alternating colors # Contains list of units def __init__(self): self.units = [] def register_unit(self, unit): self.units.append(unit) def remove_unit(self, unit): self.remove(unit) def format(self): formatted = [] # Iterate over all units. Keep count to apply alternating color schemes for count, unit in enumerate(self.units): if unit.alt_scheme is None: # Apply default color scheme, which is alternating color_scheme = COLOR_SCHEME.A1 if count % 2 else COLOR_SCHEME.A2 else: # Apply alternate color scheme color_scheme = unit.alt_scheme # INA is a special case if color_scheme == COLOR_SCHEME.INA: color_scheme = COLOR_SCHEME.A1_INA if count % 2 else COLOR_SCHEME.A2_INA close = block(click='') if unit.action is not None else '' b_color = color_scheme.back_color i_color = color_scheme.icon_color t_color = color_scheme.text_color # Start with a major separator. Keep previous background color, set foreground color # of separator to background color of this unit blocks = [] blocks.append(block(fg=b_color, append=sl)) blocks.append(' ') # TODO perhaps more nice soluting than manual spacing if len(unit.items) >= 1: (icon, text) = unit.items[0] blocks.append(block(click=unit.action, fg=i_color, bg=b_color, font='2' , append=' ' + icon)) blocks.append(' ') blocks.append(block(fg=t_color, font='1', append=text)) if len(unit.items) >= 2: for item in unit.items[1:]: (icon, text) = item # Append minor separator blocks.append(' ') blocks.append(sll) blocks.append(block(fg=i_color, bg=b_color, font='2', append=' ' + icon)) blocks.append(' ') blocks.append(block(fg=t_color, font='1', append=text)) if len(unit.items) >= 1: blocks.append(close) formatted.append(''.join(blocks)) return ' '.join(formatted) g_parser = LemonParser() def block(fg = None, bg = None, font = None, click = None, append=''): # Remeber that clickable blocks need to be closed rtn = [] if click is not None: if click == '': rtn.append('A') else: rtn.append('A:'+click+':') if fg is not None: rtn.append('F'+fg) if bg is not None: rtn.append('B'+bg) if font is not None: rtn.append('T'+font) return ''.join(['%{', ' '.join(rtn), '}', append]) reset = block(fg='-', bg='-') reset_power = block(fg='-', bg=conf.color_poweropts) class COLOR_SCHEME(Enum): A1 = (conf.color_sec_b1, conf.color_fore, conf.color_icon) A2 = (conf.color_sec_b2, conf.color_fore, conf.color_icon) INA = (None, None, None) # This one is special, dynamically changed to A1_INA or A2_INA A1_INA = (conf.color_sec_b1, conf.color_disable, conf.color_disable) A2_INA = (conf.color_sec_b2, conf.color_disable, conf.color_disable) CPU_ALERT= (conf.color_cpu, conf.color_back, conf.color_back) NET_ALERT= (conf.color_net, conf.color_back, conf.color_back) SPECIAL = (conf.color_head, conf.color_back, conf.color_back) def __init__(self, back_color, text_color, icon_color): self.back_color = back_color self.text_color = text_color self.icon_color = icon_color def single_sect(icon='', text='', click=None, alt=COLOR_SCHEME.A1): # If there is an action we need to close the block close = block(click='') if click is not None else '' if icon != '': icon = ' ' + icon return ' '.join([block(fg=alt.back_color, append=sl) , block(click=click, fg=alt.icon_color, bg=alt.back_color , font='2', append=icon) , block(fg=alt.text_color, font='1', append=text) , close]) def double_sect(text1 = '', text2 = '', icon1 = '', icon2 = '', click = None , alt=COLOR_SCHEME.A1): # If text_color None then icon_color will apply close = block(click='') if click is not None else '' return ''.join([block(fg=alt.back_color, append=sl), ' ' , block(click=click, fg=alt.icon_color, bg=alt.back_color, font='2') , ' ', icon1, block(fg=alt.text_color, font='1'), ' ', text1 , block(fg=alt.icon_color), sll, block(font='2') , icon2, block(fg=alt.text_color, font='1'), ' ', text2, ' ', close]) def control_sect(sec_color=None, head='', buttons=[], actions=[]): rtn = head for button, action in zip(buttons, actions): # If there is an action we need to close the block close = block(click='') if action is not None else '' rtn += ' '.join([block(click=action), button, close]) return rtn # Constants power_opts = ''.join([block(fg=conf.color_fore, bg=conf.color_poweropts) , ' Abort (Esc) | System (l) lock, (e) logout, (s) suspend, (h) hibernate' , ', (r) reboot, (Shift+s) shutdown']) controls = ' '.join([block(fg=conf.color_head, bg=conf.color_sec_b2) , conf.sep_right, block(fg=conf.color_head, bg=conf.color_sec_b2, click='mode cycle') , conf.icon_prog , block(fg=conf.color_sec_b2, bg='-', click='') , control_sect(head='', buttons=['on', 'off'] , actions=['bluetooth power on', 'bluetooth power off']) , control_sect(head='PXC 550', buttons=['conn.', 'disc.'] , actions=['bluetooth connect pxc550', 'bluetooth disconnect pxc550']) ]) def update_response(data): global response resp = ' '.join(data) #common.logger.debug('Got response {}'.format(resp)) response= single_sect(text=resp) def update_displays(data): global displays dsp_array = data[0].split(':') parsed_list = [block(click='displays', font='2')] for dsp in dsp_array[1:]: if dsp == 'eDP1': col_head = conf.color_head elif dsp == 'DP1': col_head = conf.color_vga elif dsp == 'HDMI2': col_head = conf.color_hdmi else: col_head = '#00000000' # Undefined parsed_list.append(block(fg=conf.color_back, bg=col_head)) parsed_list.append(conf.icon_wsp) parsed_list.append(block(click='')) displays = ' '.join(parsed_list) def update_workspaces(data): global workspaces prefix = block(font='1', fg=conf.color_back, bg=conf.color_head) prefix_foc = ''.join([block(fg = conf.color_head, bg=conf.color_wsp) , conf.sep_right , block(fg=conf.color_back, bg=conf.color_wsp, font='1')]) prefix_ina = block(fg=conf.color_back, bg=conf.color_head, font='1') wspces = [] for entry in data: # entry for example FOC5___terms status = entry[0:3] # FOC or INA num = entry[3] name = entry[7:] full_name = ' '.join(['', num, name]) current = ''.join([block(click=('i3-msg workspace' + full_name)) , full_name, block(click='')]) if status == "FOC": wspces.append(''.join([prefix_foc, current])) else: wspces.append(''.join([prefix_ina, current])) workspaces = ''.join([prefix, ' '.join(wspces)]) def update_title(data): global win_title win_title = ' '.join([block(fg=conf.color_head, bg=conf.color_sec_b2) , conf.sep_right, block(fg=conf.color_head, bg=conf.color_sec_b2, click='mode cycle') , conf.icon_prog , block(fg=conf.color_sec_b2, bg='-') , ' '.join(data)]) def format_line(): # Need to end with a reset block, otherwise the color fills the %{r} spacer if common.mode == common.bar_mode.normal: return ''.join(['%{l}', reset, displays, workspaces, win_title , '%{r}', g_parser.format(), reset]) elif common.mode == common.bar_mode.power: return ''.join(['%{l}', reset, power_opts , '%{r}', g_parser.format(), reset_power]) elif common.mode == common.bar_mode.control: return ''.join(['%{l}', reset, displays, workspaces, controls , '%{r}', response, time, reset]) else: return ''.join(['%{l}', reset, displays, workspaces, 'Not normal' , '%{r}', time, reset]) parsers_dict = { 'WSP':update_workspaces ,'WIN':update_title ,'DISP':update_displays ,'RESP':update_response } def parse_line(line_in): ''' Lines are CNK_FAST Fri 21 Sep 19:45:18 VolXXX wlan_d wlan_u eth_d eth_u CNK_SLOW cpu mem disk_root disk_home batt bri lang disp WSPINA1___main INA2___web FOC5___terms INA6___stats ''' for unit in g_parser.units: if unit.external is not None: for key,func in unit.external.items(): l = len(key) if line_in[:l] == key: func(line_in[l:].split()) break try: for key,func in parsers_dict.items(): l = len(key) if line_in[:l] == key: func(line_in[l:].split()) break except: print('Exception occured\n Line in: {}\n Data: {}'.format(line_in, line_in[l:].split())) raise formatted_line = format_line() return formatted_line if __name__ == "__main__": for line in sys.stdin: print(parse_line(line))