import sys import json import time import core.theme import core.event def dump_json(obj): return obj.dict() def assign(src, dst, key, src_key=None, default=None): if not src_key: src_key = key.replace('_', '-') # automagically replace - with _ for k in src_key if isinstance(src_key, list) else [src_key]: if k in src: dst[key] = src[k] return if default is not None: dst[key] = default class block(object): __COMMON_THEME_FIELDS = [ 'separator', 'separator-block-width', 'default-separators', 'border-top', 'border-left', 'border-right', 'border-bottom', 'pango', 'fg', 'bg', 'padding', 'prefix', 'suffix' ] def __init__(self, theme, module, widget): self.__attributes = {} for key in self.__COMMON_THEME_FIELDS: tmp = theme.get(key, widget) if tmp is not None: self.__attributes[key] = tmp self.__attributes['name'] = module.id self.__attributes['instance'] = widget.id self.__attributes['prev-bg'] = theme.get('bg', 'previous') def set(self, key, value): self.__attributes[key] = value def dict(self): result = {} assign(self.__attributes, result, 'full_text', ['full_text', 'separator']) assign(self.__attributes, result, 'separator', 'default-separators') if '_decorator' in self.__attributes: assign(self.__attributes, result, 'color', 'bg') assign(self.__attributes, result, 'background', 'prev-bg') result['_decorator'] = True else: assign(self.__attributes, result, 'color', 'fg') assign(self.__attributes, result, 'background', 'bg') if 'full_text' in self.__attributes: result['full_text'] = self.__format(self.__attributes['full_text']) for k in [ 'name', 'instance', 'separator_block_width', 'border', 'border_top', 'border_bottom', 'border_left', 'border_right' ]: assign(self.__attributes, result, k) return result def __pad(self, text): padding = self.__attributes.get('padding', '') if not text: return padding return '{}{}{}'.format(padding, text, padding) def __format(self, text): if text is None: return None return '{}{}{}'.format( self.__pad(self.__attributes.get('prefix')), text, self.__pad(self.__attributes.get('suffix')) ) class i3(object): def __init__(self, theme=core.theme.Theme(), config=core.config.Config([])): self.__modules = [] self.__content = {} self.__theme = theme self.__config = config core.event.register('start', self.draw, 'start') core.event.register('update', self.draw, 'statusline') core.event.register('stop', self.draw, 'stop') def theme(self, new_theme=None): if new_theme: self.__theme = new_theme return self.__theme def modules(self, modules=None): if not modules: return self.__modules self.__modules = modules if isinstance(modules, list) else [ modules ] def draw(self, what, args=None): cb = getattr(self, what) data = cb(args) if args else cb() if 'blocks' in data: sys.stdout.write(json.dumps(data['blocks'], default=dump_json)) if 'suffix' in data: sys.stdout.write(data['suffix']) sys.stdout.write('\n') sys.stdout.flush() def start(self): return { 'blocks': { 'version': 1, 'click_events': True }, 'suffix': '\n[', } def stop(self): return { 'suffix': '\n]' } def __separator_block(self, module, widget): if not self.__theme.get('separator'): return [] blk = block(self.__theme, module, widget) blk.set('_decorator', True) return [blk] def __content_block(self, module, widget): blk = block(self.__theme, module, widget) blk.set('min_width', widget.get('theme.minwidth')) blk.set('full_text', self.__content[widget]) if self.__config.debug(): blk.set('__state', ', '.join(module.state(widget))) return blk def blocks(self, module): blocks = [] for widget in module.widgets(): if widget.module() and self.__config.autohide(widget.module().name()): if not any(state in widget.state() for state in [ 'warning', 'critical']): continue blocks.extend(self.__separator_block(module, widget)) blocks.append(self.__content_block(module, widget)) core.event.trigger('next-widget') return blocks # TODO: only updates full text, not the state!? def update(self, affected_modules=None): now = time.time() for module in self.__modules: if affected_modules and not module.id in affected_modules: continue if not affected_modules and module.next_update: if now < module.next_update: continue module.update_wrapper() module.next_update = now + float(module.parameter('interval', self.__config.interval())) for widget in module.widgets(): self.__content[widget] = widget.full_text() def statusline(self): blocks = [] for module in self.__modules: blocks.extend(self.blocks(module)) return { 'blocks': blocks, 'suffix': ',' } def wait(self, interval): time.sleep(interval) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4