Merge branch 'master' of git://github.com/tobi-wan-kenobi/bumblebee-status

This commit is contained in:
Max Pivo 2020-02-27 12:20:02 -08:00
commit 4fdba0be19
13 changed files with 167 additions and 42 deletions

View file

@ -69,6 +69,7 @@ bar {
``` ```
# Documentation # Documentation
See [the wiki](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki) for documentation. See [the wiki](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki) for documentation.
See [FAQ](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/FAQ) for, well, FAQs. See [FAQ](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/FAQ) for, well, FAQs.
@ -79,6 +80,14 @@ Other resources:
* [How to write a theme](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/How-to-write-a-theme) * [How to write a theme](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/How-to-write-a-theme)
* [How to write a module](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/How-to-write-a-module) * [How to write a module](https://github.com/tobi-wan-kenobi/bumblebee-status/wiki/How-to-write-a-module)
User contributions:
[@somospocos:bumblebee-status-contrib](https://github.com/somospocos/bumblebee-status-contrib): Collected resources and useful tricks by @somospocos
[@somospocos:bumblebee-bridge-dwm](https://github.com/somospocos/bumblebee-bridge-dwm): Bridge bumblebee-status output into dwm status bar
[@somospocos:bumblebee-bridge-dzen2](https://github.com/somospocos/bumblebee-bridge-dzen2): Bridge bumblebee-status output into dzen2
# Installation # Installation
``` ```
$ git clone git://github.com/tobi-wan-kenobi/bumblebee-status $ git clone git://github.com/tobi-wan-kenobi/bumblebee-status
@ -183,6 +192,25 @@ For example:
`$ bumblebee-status -p disk.left-click="nautilus {instance}"` `$ bumblebee-status -p disk.left-click="nautilus {instance}"`
Advanced usage:
You can also "programmatically" trigger click events like this:
```
$ bumblebee-ctl --button <button> --id <ID of the widget> --module <name of the module>
```
For that to work, you need to know the ID of the widget - this can be configured by adding the `<name>.id` parameter - it's a comma-separated list with one entry per widget (for multi-widget modules).
Example:
```
$ bumblebee-status -m cmus -p cmus.id="prev,main,next,shuffle,repeat"
# to "simulate" a left-click on "next":
$ bumblebee-ctl --button left-mouse --module cmus --id next
```
For a full synopsis, please see `bumblebee-ctl --help`.
## Errors ## Errors
If errors occur, you should see them in the i3bar itself. If that does not work, or you need more information for troubleshooting, you can activate a debug log using the `-d` or `--debug` switch: If errors occur, you should see them in the i3bar itself. If that does not work, or you need more information for troubleshooting, you can activate a debug log using the `-d` or `--debug` switch:
@ -235,6 +263,8 @@ Modules and commandline utilities are only required for modules, the core itself
* zpool (for module 'zpool') * zpool (for module 'zpool')
* progress (for module 'progress') * progress (for module 'progress')
* i3exit (for module 'system') * i3exit (for module 'system')
* dunst (for module 'dunst')
* hddtemp (for module 'hddtemp')
# Examples # Examples
Here are some screenshots for all themes that currently exist: Here are some screenshots for all themes that currently exist:

37
bumblebee-ctl Executable file
View file

@ -0,0 +1,37 @@
#!/usr/bin/env python
import argparse
import json
import glob
import socket
button = {
'left-mouse': 1,
'middle-mouse': 2,
'right-mouse': 3,
'wheel-up': 4,
'wheel-down': 5,
}
def main():
parser = argparse.ArgumentParser(description='send commands to bumblebee-status')
parser.add_argument('-b', '--button', choices=['left-mouse', 'right-mouse', 'middle-mouse', 'wheel-up', 'wheel-down'], help='button to emulate', default='left-mouse')
parser.add_argument('-i', '--id', help='ID of widget to trigger', required=True)
parser.add_argument('-m', '--module', help='name of the module to trigger', required=True)
args = parser.parse_args()
for f in glob.glob('/tmp/.bumblebee-status.*'):
print('accessing {}'.format(f))
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect(f)
s.sendall(json.dumps({
'name': args.module,
'instance': args.id,
'button': button[args.button],
}).encode('ascii'))
if __name__ == "__main__":
main()
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4

View file

@ -67,7 +67,9 @@ def main():
import time import time
while True: while True:
output.begin() output.begin()
error = bumblebee.modules.error.Module(engine, config) error = bumblebee.modules.error.Module(engine, {
"config": config, "name": "error"
})
error.set("exception occurred: {} in {}".format(e, module)) error.set("exception occurred: {} in {}".format(e, module))
widget = error.widgets()[0] widget = error.widgets()[0]
widget.link_module(error) widget.link_module(error)

View file

@ -295,8 +295,15 @@ class Engine(object):
self._current_module = module self._current_module = module
module.update_wrapper(module.widgets()) module.update_wrapper(module.widgets())
if module.error is None: if module.error is None:
widget_ids = []
if module.parameter('id'):
widget_ids = module.parameter('id').split(',')
idx = 0
for widget in module.widgets(): for widget in module.widgets():
widget.link_module(module) widget.link_module(module)
if idx < len(widget_ids):
widget.id = widget_ids[idx]
idx = idx + 1
self._output.draw(widget=widget, module=module, engine=self) self._output.draw(widget=widget, module=module, engine=self)
else: else:
self._output.draw(widget=module.errorWidget(), module=module, engine=self) self._output.draw(widget=module.errorWidget(), module=module, engine=self)

View file

@ -1,9 +1,11 @@
"""Input classes""" """Input classes"""
import os
import sys import sys
import json import json
import uuid import uuid
import time import time
import socket
import select import select
import logging import logging
import threading import threading
@ -23,38 +25,62 @@ def is_terminated():
return True return True
return False return False
class CommandSocket(object):
def __init__(self):
self._name = "/tmp/.bumblebee-status.{}".format(os.getpid())
self._socket = None
def __enter__(self):
self._socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
self._socket.bind(self._name)
self._socket.listen(5)
return self._socket
def __exit__(self, type, value, traceback):
self._socket.close()
os.unlink(self._name)
def read_input(inp): def read_input(inp):
"""Read i3bar input and execute callbacks""" """Read i3bar input and execute callbacks"""
poll = select.poll()
poll.register(sys.stdin.fileno(), select.POLLIN)
log.debug("starting click event processing")
while inp.running:
if is_terminated():
return
try: with CommandSocket() as cmdsocket:
events = poll.poll(1000) poll = select.poll()
except Exception: poll.register(sys.stdin, select.POLLIN)
continue poll.register(cmdsocket, select.POLLIN)
for fileno, event in events: log.debug("starting click event processing")
line = "[" while inp.running:
while line.startswith("["): if is_terminated():
line = sys.stdin.readline().strip(",").strip() return
log.debug("new event: {}".format(line))
inp.has_event = True
try: try:
event = json.loads(line) events = poll.poll(1000)
if "instance" in event: except Exception:
inp.callback(event) continue
inp.redraw() for fileno, event in events:
if fileno == cmdsocket.fileno():
tmp, _ = cmdsocket.accept()
line = tmp.recv(4096).decode()
tmp.close()
else: else:
log.debug("field 'instance' missing in input, not processing the event") line = "["
except ValueError as e: while line.startswith("["):
log.debug("failed to parse event: {}".format(e)) line = sys.stdin.readline().strip(",").strip()
log.debug("exiting click event processing") log.debug("new event: {}".format(line))
poll.unregister(sys.stdin.fileno()) inp.has_event = True
inp.has_event = True try:
inp.clean_exit = True event = json.loads(line)
if "instance" in event:
inp.callback(event)
inp.redraw()
else:
log.debug("field 'instance' missing in input, not processing the event")
except ValueError as e:
log.debug("failed to parse event: {}".format(e))
log.debug("exiting click event processing")
poll.unregister(sys.stdin.fileno())
inp.has_event = True
inp.clean_exit = True
class I3BarInput(object): class I3BarInput(object):
"""Process incoming events from the i3bar""" """Process incoming events from the i3bar"""

View file

@ -240,7 +240,7 @@ class Module(bumblebee.engine.Module):
def state(self, widget): def state(self, widget):
state = [] state = []
capacity = widget.get("capacity") capacity = widget.get("capacity", -1)
if capacity < 0: if capacity < 0:
return ["critical", "unknown"] return ["critical", "unknown"]

View file

@ -91,17 +91,16 @@ class Module(bumblebee.engine.Module):
def cpuload(self, _): def cpuload(self, _):
return "{}%".format(self._cpuload) return "{}%".format(self._cpuload)
@staticmethod def add_color(self, bar):
def add_color(bar):
"""add color as pango markup to a bar""" """add color as pango markup to a bar"""
if bar in ["", ""]: if bar in ["", ""]:
color = "green" color = self.theme().color("green", "green")
elif bar in ["", ""]: elif bar in ["", ""]:
color = "yellow" color = self.theme().color("yellow", "yellow")
elif bar in ["", ""]: elif bar in ["", ""]:
color = "orange" color = self.theme().color("orange", "orange")
elif bar in ["", ""]: elif bar in ["", ""]:
color = "red" color = self.theme().color("red", "red")
colored_bar = "<span foreground='{}'>{}</span>".format(color, bar) colored_bar = "<span foreground='{}'>{}</span>".format(color, bar)
return colored_bar return colored_bar

View file

@ -142,12 +142,12 @@ class Module(bumblebee.engine.Module):
vol, bumblebee.output.hbar(float(self._left))) vol, bumblebee.output.hbar(float(self._left)))
return vol return vol
else: else:
vol = "{}%/{}%".format(self._left, self._right)
if self._showbars: if self._showbars:
vol = "{} {}{}".format( vol = "{} {}{}".format(
vol, vol,
bumblebee.output.hbar(float(self._left)), bumblebee.output.hbar(float(self._left)),
bumblebee.output.hbar(float(self._right))) bumblebee.output.hbar(float(self._right)))
vol = "{}%/{}%".format(self._left, self._right)
return vol return vol
def update(self, widgets): def update(self, widgets):

View file

@ -18,6 +18,7 @@ import logging
import bumblebee.input import bumblebee.input
import bumblebee.output import bumblebee.output
import bumblebee.engine import bumblebee.engine
import bumblebee.popup_v2
import functools import functools
class Module(bumblebee.engine.Module): class Module(bumblebee.engine.Module):
@ -76,7 +77,7 @@ class Module(bumblebee.engine.Module):
try: try:
bumblebee.util.execute("nmcli c up \"{vpn}\"" bumblebee.util.execute("nmcli c up \"{vpn}\""
.format(vpn=self._connected_vpn_profile)) .format(vpn=self._selected_vpn_profile))
self._connected_vpn_profile = name self._connected_vpn_profile = name
except Exception as e: except Exception as e:
logging.exception("Couldn't establish VPN connection") logging.exception("Couldn't establish VPN connection")

View file

@ -72,6 +72,12 @@ def scrollable(func):
if width < 0: if width < 0:
return text return text
if len(text) <= width: if len(text) <= width:
# do alignment
align = module.parameter("theme.align", "left")
if align == "right":
text = "{:>{}}".format(text, width)
if align == "center":
text = "{:^{}}".format(text, width)
return text return text
# we need to shorten # we need to shorten

View file

@ -186,9 +186,14 @@ class Theme(object):
result[key] = colors[field][key] result[key] = colors[field][key]
return result return result
def color(self, color_name, default=None):
return self._colorset.get(color_name, default)
def _load_colors(self, name): def _load_colors(self, name):
"""Load colors for a theme""" """Load colors for a theme"""
try: try:
if isinstance(name, dict):
return name
if name.lower() == "wal": if name.lower() == "wal":
return self._load_wal_colors() return self._load_wal_colors()
except Exception as err: except Exception as err:
@ -285,7 +290,7 @@ class Theme(object):
if isinstance(value, (dict, list)): if isinstance(value, (dict, list)):
return value return value
return self._colorset.get(value, value) return self.color(value, value)
# algorithm copied from # algorithm copied from
# http://blog.impressiver.com/post/31434674390/deep-merge-multiple-python-dicts # http://blog.impressiver.com/post/31434674390/deep-merge-multiple-python-dicts

View file

@ -1,13 +1,19 @@
{ {
"icons": [ "paxy97", "awesome-fonts" ], "icons": [ "paxy97", "awesome-fonts" ],
"colors": [{
"fg0": "#fbf1c7",
"bg0_h": "#1d2021",
"yellow": "#d79921",
"red": "#cc241d"
}],
"defaults": { "defaults": {
"warning": { "warning": {
"fg": "#1d2021", "fg": "bg0_h",
"bg": "#d79921" "bg": "yellow"
}, },
"critical": { "critical": {
"fg": "#fbf1c7", "fg": "fg0",
"bg": "#cc241d" "bg": "red"
}, },
"default-separators": false, "default-separators": false,
"separator-block-width": 0 "separator-block-width": 0

View file

@ -1,5 +1,11 @@
{ {
"icons": [ "awesome-fonts" ], "icons": [ "awesome-fonts" ],
"colors": [{
"red": "#BF616A",
"orange": "#D08770",
"yellow": "#EBCB8B",
"green": "#A3BE8C"
}],
"defaults": { "defaults": {
"separator-block-width": 0, "separator-block-width": 0,
"warning": { "warning": {