2020-02-01 21:37:38 +01:00
|
|
|
import sys
|
2020-01-19 13:29:34 +01:00
|
|
|
import json
|
2020-01-26 13:31:20 +01:00
|
|
|
import time
|
2020-01-19 13:29:34 +01:00
|
|
|
|
2020-02-09 13:46:56 +01:00
|
|
|
import core.theme
|
2020-02-16 14:30:45 +01:00
|
|
|
import core.event
|
2020-02-09 13:46:56 +01:00
|
|
|
|
2020-04-30 20:18:42 +02:00
|
|
|
import util.format
|
|
|
|
|
2020-05-03 11:15:52 +02:00
|
|
|
|
2020-04-05 10:53:17 +02:00
|
|
|
def dump_json(obj):
|
2020-04-05 13:58:59 +02:00
|
|
|
return obj.dict()
|
|
|
|
|
2020-05-03 11:15:52 +02:00
|
|
|
|
2020-04-05 13:58:59 +02:00
|
|
|
def assign(src, dst, key, src_key=None, default=None):
|
|
|
|
if not src_key:
|
2020-05-03 11:15:52 +02:00
|
|
|
if key.startswith("_"):
|
2020-05-01 15:33:31 +02:00
|
|
|
src_key = key
|
|
|
|
else:
|
2020-05-03 11:15:52 +02:00
|
|
|
src_key = key.replace("_", "-") # automagically replace _ with -
|
2020-04-05 13:58:59 +02:00
|
|
|
|
|
|
|
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
|
2020-04-05 10:53:17 +02:00
|
|
|
|
2020-05-03 11:15:52 +02:00
|
|
|
|
2020-04-05 10:53:17 +02:00
|
|
|
class block(object):
|
|
|
|
__COMMON_THEME_FIELDS = [
|
2020-05-03 11:15:52 +02:00
|
|
|
"separator",
|
|
|
|
"separator-block-width",
|
|
|
|
"default-separators",
|
|
|
|
"border-top",
|
|
|
|
"border-left",
|
|
|
|
"border-right",
|
|
|
|
"border-bottom",
|
|
|
|
"fg",
|
|
|
|
"bg",
|
|
|
|
"padding",
|
|
|
|
"prefix",
|
|
|
|
"suffix",
|
2020-04-05 10:53:17 +02:00
|
|
|
]
|
2020-05-03 11:15:52 +02:00
|
|
|
|
2020-04-05 10:53:17 +02:00
|
|
|
def __init__(self, theme, module, widget):
|
|
|
|
self.__attributes = {}
|
|
|
|
for key in self.__COMMON_THEME_FIELDS:
|
|
|
|
tmp = theme.get(key, widget)
|
2020-04-05 13:58:59 +02:00
|
|
|
if tmp is not None:
|
2020-04-05 10:53:17 +02:00
|
|
|
self.__attributes[key] = tmp
|
|
|
|
|
2020-05-03 11:15:52 +02:00
|
|
|
self.__attributes["name"] = module.id
|
|
|
|
self.__attributes["instance"] = widget.id
|
|
|
|
self.__attributes["prev-bg"] = theme.get("bg", "previous")
|
2020-04-05 10:53:17 +02:00
|
|
|
|
|
|
|
def set(self, key, value):
|
|
|
|
self.__attributes[key] = value
|
|
|
|
|
2020-04-05 14:27:09 +02:00
|
|
|
def is_pango(self, attr):
|
2020-05-03 11:15:52 +02:00
|
|
|
if isinstance(attr, dict) and "pango" in attr:
|
2020-04-05 14:27:09 +02:00
|
|
|
return True
|
|
|
|
return False
|
|
|
|
|
|
|
|
def pangoize(self, text):
|
|
|
|
if not self.is_pango(text):
|
|
|
|
return text
|
2020-05-03 11:15:52 +02:00
|
|
|
self.__attributes["markup"] = "pango"
|
|
|
|
attr = dict(text["pango"])
|
|
|
|
text = attr.get("full_text", "")
|
|
|
|
if "full_text" in attr:
|
|
|
|
del attr["full_text"]
|
|
|
|
result = "<span"
|
2020-04-05 14:27:09 +02:00
|
|
|
for key, value in attr.items():
|
|
|
|
result = '{} {}="{}"'.format(result, key, value)
|
2020-05-03 11:15:52 +02:00
|
|
|
result = "{}>{}</span>".format(result, text)
|
2020-04-05 14:27:09 +02:00
|
|
|
return result
|
|
|
|
|
2020-04-05 13:58:59 +02:00
|
|
|
def dict(self):
|
|
|
|
result = {}
|
|
|
|
|
2020-05-03 11:15:52 +02:00
|
|
|
assign(self.__attributes, result, "full_text", ["full_text", "separator"])
|
|
|
|
assign(self.__attributes, result, "separator", "default-separators")
|
2020-04-05 13:58:59 +02:00
|
|
|
|
2020-05-03 11:15:52 +02:00
|
|
|
if "_decorator" in self.__attributes:
|
|
|
|
assign(self.__attributes, result, "color", "bg")
|
|
|
|
assign(self.__attributes, result, "background", "prev-bg")
|
|
|
|
result["_decorator"] = True
|
2020-04-05 13:58:59 +02:00
|
|
|
else:
|
2020-05-03 11:15:52 +02:00
|
|
|
assign(self.__attributes, result, "color", "fg")
|
|
|
|
assign(self.__attributes, result, "background", "bg")
|
2020-04-05 13:58:59 +02:00
|
|
|
|
2020-05-03 11:15:52 +02:00
|
|
|
if "full_text" in self.__attributes:
|
|
|
|
result["full_text"] = self.pangoize(result["full_text"])
|
|
|
|
result["full_text"] = self.__format(self.__attributes["full_text"])
|
2020-04-05 13:58:59 +02:00
|
|
|
|
|
|
|
for k in [
|
2020-05-03 11:15:52 +02:00
|
|
|
"name",
|
|
|
|
"instance",
|
|
|
|
"separator_block_width",
|
|
|
|
"border",
|
|
|
|
"border_top",
|
|
|
|
"border_bottom",
|
|
|
|
"border_left",
|
|
|
|
"border_right",
|
|
|
|
"markup",
|
|
|
|
"_raw",
|
|
|
|
"_suffix",
|
|
|
|
"_prefix",
|
|
|
|
"min_width",
|
2020-05-03 11:42:14 +02:00
|
|
|
"align",
|
2020-04-05 13:58:59 +02:00
|
|
|
]:
|
|
|
|
assign(self.__attributes, result, k)
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
def __pad(self, text):
|
2020-05-03 11:15:52 +02:00
|
|
|
padding = self.__attributes.get("padding", "")
|
|
|
|
if not text:
|
|
|
|
return padding
|
|
|
|
return "{}{}{}".format(padding, text, padding)
|
2020-04-05 13:58:59 +02:00
|
|
|
|
|
|
|
def __format(self, text):
|
2020-05-03 11:15:52 +02:00
|
|
|
if text is None:
|
|
|
|
return None
|
|
|
|
prefix = self.__pad(self.pangoize(self.__attributes.get("prefix")))
|
|
|
|
suffix = self.__pad(self.pangoize(self.__attributes.get("suffix")))
|
|
|
|
self.set("_prefix", prefix)
|
|
|
|
self.set("_suffix", suffix)
|
|
|
|
self.set("_raw", text)
|
|
|
|
return "{}{}{}".format(prefix, text, suffix)
|
|
|
|
|
2020-04-05 10:53:17 +02:00
|
|
|
|
2020-01-19 13:29:34 +01:00
|
|
|
class i3(object):
|
2020-03-15 14:02:48 +01:00
|
|
|
def __init__(self, theme=core.theme.Theme(), config=core.config.Config([])):
|
2020-04-04 13:57:42 +02:00
|
|
|
self.__modules = []
|
2020-04-05 10:53:17 +02:00
|
|
|
self.__content = {}
|
2020-04-04 13:57:42 +02:00
|
|
|
self.__theme = theme
|
|
|
|
self.__config = config
|
2020-05-03 11:15:52 +02:00
|
|
|
core.event.register("update", self.update)
|
|
|
|
core.event.register("start", self.draw, "start")
|
|
|
|
core.event.register("draw", self.draw, "statusline")
|
|
|
|
core.event.register("stop", self.draw, "stop")
|
2020-02-01 21:37:38 +01:00
|
|
|
|
2020-06-23 20:03:17 +02:00
|
|
|
def content(self):
|
|
|
|
return self.__content
|
|
|
|
|
2020-03-01 14:08:16 +01:00
|
|
|
def theme(self, new_theme=None):
|
|
|
|
if new_theme:
|
2020-04-04 13:57:42 +02:00
|
|
|
self.__theme = new_theme
|
|
|
|
return self.__theme
|
2020-03-01 14:08:16 +01:00
|
|
|
|
2020-02-02 14:18:13 +01:00
|
|
|
def modules(self, modules=None):
|
|
|
|
if not modules:
|
2020-04-04 13:57:42 +02:00
|
|
|
return self.__modules
|
2020-05-03 11:15:52 +02:00
|
|
|
self.__modules = modules if isinstance(modules, list) else [modules]
|
2020-02-02 14:18:13 +01:00
|
|
|
|
2020-06-23 15:51:14 +02:00
|
|
|
def toggle_minimize(self, event):
|
2020-06-23 20:03:17 +02:00
|
|
|
widget_id = event["instance"]
|
|
|
|
|
|
|
|
if widget_id in self.__content:
|
|
|
|
self.__content[widget_id]["minimized"] = not self.__content[widget_id]["minimized"]
|
2020-06-23 15:51:14 +02:00
|
|
|
|
2020-02-08 14:22:43 +01:00
|
|
|
def draw(self, what, args=None):
|
|
|
|
cb = getattr(self, what)
|
|
|
|
data = cb(args) if args else cb()
|
2020-05-03 11:15:52 +02:00
|
|
|
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")
|
2020-02-02 21:21:24 +01:00
|
|
|
sys.stdout.flush()
|
2020-02-01 21:37:38 +01:00
|
|
|
|
2020-01-19 13:29:34 +01:00
|
|
|
def start(self):
|
2020-02-01 21:37:38 +01:00
|
|
|
return {
|
2020-05-03 11:15:52 +02:00
|
|
|
"blocks": {"version": 1, "click_events": True},
|
|
|
|
"suffix": "\n[",
|
2020-02-01 21:37:38 +01:00
|
|
|
}
|
2020-01-19 13:29:34 +01:00
|
|
|
|
|
|
|
def stop(self):
|
2020-05-03 11:15:52 +02:00
|
|
|
return {"suffix": "\n]"}
|
2020-02-01 21:37:38 +01:00
|
|
|
|
2020-06-20 14:51:49 +02:00
|
|
|
def separator_block(self, module, widget):
|
2020-05-03 11:15:52 +02:00
|
|
|
if not self.__theme.get("separator"):
|
2020-04-05 13:58:59 +02:00
|
|
|
return []
|
2020-04-05 10:53:17 +02:00
|
|
|
blk = block(self.__theme, module, widget)
|
2020-05-03 11:15:52 +02:00
|
|
|
blk.set("_decorator", True)
|
2020-04-05 13:58:59 +02:00
|
|
|
return [blk]
|
2020-04-05 10:53:17 +02:00
|
|
|
|
|
|
|
def __content_block(self, module, widget):
|
|
|
|
blk = block(self.__theme, module, widget)
|
2020-05-03 11:15:52 +02:00
|
|
|
minwidth = widget.theme("minwidth")
|
2020-05-01 20:25:10 +02:00
|
|
|
if minwidth is not None:
|
|
|
|
try:
|
2020-05-03 11:15:52 +02:00
|
|
|
blk.set("min-width", "-" * int(minwidth))
|
2020-05-01 20:25:10 +02:00
|
|
|
except:
|
2020-05-03 11:15:52 +02:00
|
|
|
blk.set("min-width", minwidth)
|
2020-05-03 11:41:01 +02:00
|
|
|
blk.set("align", widget.theme("align"))
|
2020-06-23 20:03:17 +02:00
|
|
|
blk.set("full_text", "\u2026" if self.__content[widget.id]["minimized"] else self.__content[widget.id]["text"])
|
2020-05-03 11:15:52 +02:00
|
|
|
if widget.get("pango", False):
|
|
|
|
blk.set("markup", "pango")
|
2020-04-05 10:53:17 +02:00
|
|
|
if self.__config.debug():
|
2020-04-28 20:25:28 +02:00
|
|
|
state = module.state(widget)
|
|
|
|
if isinstance(state, list):
|
2020-05-03 11:15:52 +02:00
|
|
|
state = ", ".join(state)
|
|
|
|
blk.set("__state", state)
|
2020-04-05 10:53:17 +02:00
|
|
|
return blk
|
|
|
|
|
|
|
|
def blocks(self, module):
|
|
|
|
blocks = []
|
2020-02-09 13:30:40 +01:00
|
|
|
for widget in module.widgets():
|
2020-04-30 12:50:24 +02:00
|
|
|
if widget.module and self.__config.autohide(widget.module.name):
|
2020-05-03 11:15:52 +02:00
|
|
|
if not any(
|
|
|
|
state in widget.state() for state in ["warning", "critical"]
|
|
|
|
):
|
2020-03-15 14:01:09 +01:00
|
|
|
continue
|
2020-04-29 14:39:12 +02:00
|
|
|
if module.hidden():
|
|
|
|
continue
|
2021-01-17 15:21:40 +01:00
|
|
|
if "critical" in widget.state() and self.__config.errorhide(widget.module.name):
|
|
|
|
continue
|
2020-06-20 14:51:49 +02:00
|
|
|
blocks.extend(self.separator_block(module, widget))
|
2020-04-05 10:53:17 +02:00
|
|
|
blocks.append(self.__content_block(module, widget))
|
2020-05-03 11:15:52 +02:00
|
|
|
core.event.trigger("next-widget")
|
2020-04-05 10:53:17 +02:00
|
|
|
return blocks
|
2020-02-09 13:30:40 +01:00
|
|
|
|
2020-08-28 17:14:05 +02:00
|
|
|
def update(self, affected_modules=None, redraw_only=False, force=False):
|
2020-03-23 15:32:06 +01:00
|
|
|
now = time.time()
|
2020-04-04 13:57:42 +02:00
|
|
|
for module in self.__modules:
|
2020-03-29 14:43:04 +02:00
|
|
|
if affected_modules and not module.id in affected_modules:
|
2020-02-23 21:13:49 +01:00
|
|
|
continue
|
2020-03-23 15:32:06 +01:00
|
|
|
if not affected_modules and module.next_update:
|
2020-08-28 17:14:05 +02:00
|
|
|
if now < module.next_update and not force:
|
2020-03-23 15:32:06 +01:00
|
|
|
continue
|
2020-06-25 20:34:13 +02:00
|
|
|
|
2020-04-12 14:44:02 +02:00
|
|
|
if not redraw_only:
|
|
|
|
module.update_wrapper()
|
2020-05-03 11:15:52 +02:00
|
|
|
if module.parameter("interval", "") != "never":
|
|
|
|
module.next_update = now + util.format.seconds(
|
|
|
|
module.parameter("interval", self.__config.interval())
|
|
|
|
)
|
2020-06-25 20:34:13 +02:00
|
|
|
else:
|
|
|
|
module.next_update = sys.maxsize
|
2020-02-24 15:05:58 +01:00
|
|
|
for widget in module.widgets():
|
2020-06-23 20:03:17 +02:00
|
|
|
if not widget.id in self.__content:
|
|
|
|
self.__content[widget.id] = { "minimized": False }
|
|
|
|
self.__content[widget.id]["text"] = widget.full_text()
|
2020-02-09 13:25:34 +01:00
|
|
|
|
|
|
|
def statusline(self):
|
2020-04-05 10:53:17 +02:00
|
|
|
blocks = []
|
2020-04-04 13:57:42 +02:00
|
|
|
for module in self.__modules:
|
2020-04-05 10:53:17 +02:00
|
|
|
blocks.extend(self.blocks(module))
|
2020-05-03 11:15:52 +02:00
|
|
|
return {"blocks": blocks, "suffix": ","}
|
2020-01-19 13:29:34 +01:00
|
|
|
|
2020-01-26 13:31:20 +01:00
|
|
|
def wait(self, interval):
|
|
|
|
time.sleep(interval)
|
|
|
|
|
2020-05-03 11:15:52 +02:00
|
|
|
|
2020-01-19 13:29:34 +01:00
|
|
|
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|