1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
|
xob - X Overlay Bar
===================

A lightweight configurable overlay volume/backlight/progress/anything bar for the X Window System (and Wayland compositors with XWayland). Each time a new value is read on the standard input, it is displayed as a tv-like bar over other windows. It then vanishes after a configurable amount of time. A value followed by a bang '!' is displayed using an alternate color to account for special states (e.g. muted audio). There is also support for overflows (when the value exceeds the maximum).

## Installation
xob depends on libx11, libxrender (for optional transparency support) and libconfig.
make
make install
To build xob without transparency support and rely only on libx11 and libconfig: `make enable_alpha=no`.
Packages are available in the following repositories:
[](https://repology.org/project/xob/versions)
## Usage
xob [-m maximum] [-t timeout] [-c configfile] [-s style]
* **maximum** Maximum value/number of steps in the bar (default: 100). 0 is always the minimum.
* **timeout** Duration in milliseconds the bar remains on-screen after an update (default: 1000). 0 means the bar is never hidden.
* **configfile** Path to a file that specifies styles (appearances).
* **style** Chosen style from the configuration (default: the style named "default").
### Try it out
Launch xob in a terminal, enter a value (positive integer), press return. Suffix a value with '!' for alternate color. Use a value above the maximum (default: 100) to see how overflows are displayed.
### General case
The ideal way to use xob is to have a program (see example scripts below) that listens to events (such as a change in audio volume levels) and issues new values on the standard output automatically. Launch `the_listener_program | xob` and everything works out of the box.
### Ready to use volume bar
If you are using pulseaudio, save the following Python script (depends on the pulsectl python library) and simply pipe it in xob! `./pulse-volume-watcher.py | xob`
```python
#!/usr/bin/env python3
from pulsectl import Pulse, PulseLoopStop
import sys
with Pulse() as pulse:
def callback(ev):
if ev.index == sink_index: raise PulseLoopStop
def current_status(sink):
return round(sink.volume.value_flat * 100), sink.mute == 1
try:
sinks = {s.index:s for s in pulse.sink_list()}
if len(sys.argv) > 1:
# Sink index from command line argument if provided
sink_index = int(sys.argv[1])
if not sink_index in sinks:
raise KeyError(f"Sink index {sink_index} not found in list of sinks.")
else:
# Automatic determination of default sink otherwise
default_sink_name = pulse.server_info().default_sink_name
try:
sink_index = next(index for index, sink in sinks.items()
if sink.name == default_sink_name)
except StopIteration: raise StopIteration("No default sink was found.")
pulse.event_mask_set('sink')
pulse.event_callback_set(callback)
last_value, last_mute = current_status(sinks[sink_index])
while True:
pulse.event_listen()
sinks = {s.index:s for s in pulse.sink_list()}
value, mute = current_status(sinks[sink_index])
if value != last_value or mute != last_mute:
print(str(value) + ('!' if mute else ''))
last_value, last_mute = value, mute
sys.stdout.flush()
except Exception as e:
print(f"ERROR: {e}", file=sys.stderr)
```
This script listens to volume and mute events. No matter how the volume changes (keybindings, pulse control panel, headphones plugged-in), it will instantly show up the volume bar. It should work out of the box but you can optionally pass the index of a sink on the command line, as given by `pacmd list-sinks`.
### Ready to use brightness bar
One can access the brightness value from `/sys/class/backlight/video_backlight/brightness` (where `video` is your video device). The following script watches for modifications on that file using the watchdog python library. No matter how the brightness changes, this script will return the new brightness value. You may have to change the path of `brightness_file` if you are not using an Intel device. Simply pipe it in xob and you are ready to go. `./brightness-watcher.py | xob`.
```python
#!/usr/bin/env python3
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler, FileModifiedEvent
import sys
import time
brightness_file = '/sys/class/backlight/intel_backlight/brightness'
max_brightness_file ='/sys/class/backlight/intel_backlight/max_brightness'
with open(max_brightness_file, 'r') as f:
maxvalue = int(f.read())
def notify(file_path):
with open(file_path, 'r') as f:
value = int(int(f.read())/maxvalue*100)
print(value)
class Handler(FileSystemEventHandler):
def on_modified(self, event):
if isinstance(event, FileModifiedEvent):
notify(event.src_path)
handler = Handler()
observer = Observer()
observer.schedule(handler, path=brightness_file)
observer.start()
try:
while True:
sys.stdout.flush()
time.sleep(1)
except KeyboardInterrupt:
observer.stop()
observer.join()
```
### Fallback method
In case no input program fits your needs, you may trigger changes manually. Append new values in a named pipe (a pipe that persists as a special file on the filesystem) and have xob consume them as they arrive. **Warning!** This method should be considered as fallback: it is more cumbersome to set up and likely to miss changes you would like displayed on the bar.
Create a named pipe, e.g. */tmp/xobpipe*, on your filesystem.
mkfifo /tmp/xobpipe
Have xob consume new values as they arrive on the pipe.
tail -f /tmp/xobpipe | xob
Write values to the pipe when you deem it relevant. In the classic audio volume bar example, that would be after the user has pressed a button and you changed the volume (usually set up as a keybinding in your window manager or desktop environment).
command_that_outputs_a_value >> /tmp/xobpipe
To try it manually, issue a test value such as `echo 43 >> /tmp/xobpipe`.
## Appearance
When starting, xob looks for the configuration file in the following order:
1. The path specified as the **-c** argument.
2. `$XDG_CONFIG_HOME$/xob/styles.cfg` (if `$XDG_CONFIG_HOME$` is set)
3. `~/.config/xob/styles.cfg`
4. Under the system configuration directory (determined during build process): e.g. /etc/xob/styles.cfg or /usr/local/etc/xob/styles.cfg
Consult the man page for detailed information about the configuration file and the available options. The following `styles.cfg` defines a single style called "default" that showcases all the possible options set to the default values. The configuration file may contain additional styles to choose among using the **-s** argument.
default = {
x = {relative = 1; offset = -48;};
y = {relative = 0.5; offset = 0;};
length = {relative = 0.3; offset = 0;};
thickness = 24;
outline = 3;
border = 4;
padding = 3;
orientation = "vertical";
overflow = "proportional";
color = {
normal = {
fg = "#ffffff";
bg = "#00000090";
border = "#ffffff";
};
alt = {
fg = "#555555";
bg = "#00000090";
border = "#555555";
};
overflow = {
fg = "#ff0000";
bg = "#00000090";
border = "#ff0000";
};
altoverflow = {
fg = "#550000";
bg = "#00000090";
border = "#550000";
};
};
};
### Orientation
The bar can be horizontal (it fills up from left to right) or vertical (it fills up from bottom to top) depending on option `orientation`. The default value is `"vertical"`.
### Positionning
The horizontal `x` and vertical `y` position of the bar is determined by a relative position on the screen (0.0 and 1.0 on the edges, 0.5 in the middle) and an offset in pixels from that relative position. Likewise, the length of the bar has a relative component (1.0 all available space, 0.0 collapsed) and an offset in pixels. Here are some examples.
Illustration | Relative `x` | Offset `x` | Relative `y` | Offset `y` | Relative `length` | Offset `length`
------------ | ------------ | ---------- | ------------ | ---------- | ----------------- | ---------------
 | 0.5 | 0 | 0.75 | 0 | 0.3 | 0
 | 0.0 | 0 | 0.0 | 0 | 0.3 | 0
 | 1.0 | 0 | 0.0 | 0 | 0.3 | 0
 | 0.5 | 0 | 0.0 | 0 | 0.3 | 0
 | 0.5 | 0 | 1.0 | 0 | 0.3 | 0
 | 1.0 | -10 | 1.0 | -10 | 0.3 | 0
 | 0.5 | 0 | 0.75 | 0 | 0.5 | 0
 | 0.5 | 0 | 0.75 | 0 | 1 | 0
 | 0.5 | 0 | 0.75 | 0 | 1 | -20
 | 0.0 | +110 | 0.0 | +135 | 0.0 | +126
### Size and borders

The values for thickness, padding, border, and outline are a number of pixels.
### Colors and overflows

There are three colors: foreground, background, and border. They change depending on the displayed value. Also, there are two ways to display overflows: use the overflow color with no feedback on how much it overflows ("hidden" mode), or use the overflow color on part of the bar only proportionally to how much the value overflows ("proportional" mode).
Illustration | Display mode | Overflow type
------------ | ------------------ | -------------
 | Normal | No overflow (empty)
 | Normal | No overflow (quarter-full)
 | Alternate (e.g. mute) | No overflow (quarter-full)
 | Normal | No overflow (full)
 | Normal | Overflow in "hidden" mode
 | Normal | Overflow in "proportional" mode
 | Alternate | Overflow in "hidden" mode
 | Alternate | Overflow in "proportional" mode
### i3wm

Here is a style that integrates well into default i3wm. Add it to your `styles.cfg`
i3 = {
padding = 0;
border = 1;
outline = 0;
thickness = 24;
color = {
normal = {
fg = "#285577";
bg = "#222222";
border = "#4c7899";
};
alt = {
fg = "#333333";
bg = "#222222";
border = "#444444";
};
overflow = {
fg = "#900000";
bg = "#222222";
border = "#933333";
};
altoverflow = {
fg = "#900000";
bg = "#222222";
border = "#444444";
};
};
};
## FAQ
> "How should I display different sources of information (e.g. volume and brightness)?"
> "What happens if several bars are displayed at the same time?"
You can run and distinguish two or more instances of xob with different styles (including color, position, extreme values, etc.). To do so, specify and use different styles from your configuration file (or use different configuration files). To prevent the bars from overlapping, make use of the offset options. For instance, in horizontal mode, you can offset a bar to the top or bottom (see the following example configuration file).
volume = {
thickness = 24;
outline = 1;
border = 2;
padding = 0;
y = {
relative = 0.9;
offset = 0;
};
orientation = "horizontal";
};
backlight = {
thickness = 24;
outline = 1;
border = 2;
padding = 0;
y = {
relative = 0.9;
# To prevent overlap with the volume bar if displayed at the same time
offset = -30;
};
orientation = "horizontal";
color = {
normal = {
fg = "#0000ff";
bg = "#000000";
border = "#0000ff";
};
};
};
> "Can I integrate xob in a panel of my desktop environment or window manager?"
There is no support for panel integration. You can however use absolute positioning and no timeout (`-t 0`) to mimic this behaviour in simple situations.
> "How about multiple monitors?"
xob works well under multihead setups. The `x`, `y`, and `length` style options refer to the combined screen surface. By default the bar is vertical near the right edge of the rightmost monitor. In vertical layouts, you may prefer to switch the bar to horizontal mode as follows to avoid splits.
horizontal = {
x = {relative = 0.5; offset = 0;};
y = {relative = 1; offset = -48;};
orientation = "horizontal";
};
## License
GPL3, see [LICENSE](/LICENSE).
|