Compare commits

...

7 commits
v0.0.2 ... main

7 changed files with 150 additions and 122 deletions

View file

@ -24,6 +24,15 @@ cd easyconf-lemonbar
sudo sh install.sh sudo sh install.sh
``` ```
To uninstall from your system:
```sh
sudo sh uninstall.sh
```
This will leave your config file in place, should you ever reinstall eclb.
**To update: uninstall, then follow the installation instructions again.**
## Configuration and Usage ## Configuration and Usage
### Config File ### Config File
After installing, you will need to copy the example config file to your After installing, you will need to copy the example config file to your
@ -49,18 +58,22 @@ easyconf-lemonbar process. First, in the module configuration, specify which
signal to listen for, e.g. signal to listen for, e.g.
```yaml ```yaml
...
# Display a random number, and only update when we recieve the RTMIN+1 signal. # Display a random number, and only update when we recieve the RTMIN+1 signal.
- name: "random" random:
command: "python -c 'from random import randrange; print(randrange(1000))'" command: "python -c 'from random import randrange; print(randrange(1000))'"
refresh: 0 refresh: 0
signal: 1 signal: 1
format: format:
align: left align: left
...
``` ```
In the example above, the refresh is `0`. This means that the module *will never In the example above, the refresh is `0`. This means that the module *will never
refresh on its own*; it will wait for the RTMIN+1 signal. You may also configure refresh on its own*; it will wait for the RTMIN+1 signal. You may also configure
your module to both refresh on a time interval AND listen for a signal. your module to both refresh on a time interval AND listen for a signal. Also
note: multiple modules may listen for the same signal. They will all be
refreshed when the signal is recieved.
Unfortunately, to send the signal, you cant just `pkill easyconf-lemonbar`. You Unfortunately, to send the signal, you cant just `pkill easyconf-lemonbar`. You
have to read the PID from eclb's pidfile. I would recommend creating an alias in have to read the PID from eclb's pidfile. I would recommend creating an alias in

View file

@ -2,8 +2,8 @@
# where indicated. # where indicated.
bar: bar:
# Global configuration options. These are mostly flags passed to lemonbar at # Global configuration options. These are mostly flags passed to lemonbar at
# runtime. # runtime. These are all optional.
config: #config:
# Define a custom size of the bar. By default, this is different for every # Define a custom size of the bar. By default, this is different for every
# screen, so the format is shown below. # screen, so the format is shown below.
#geometry: "widthxheight+x+y" #geometry: "widthxheight+x+y"
@ -51,65 +51,68 @@ bar:
# Text to be placed on both ends of the bar, before and after all text. # Text to be placed on both ends of the bar, before and after all text.
#margin: "" #margin: ""
# Modules are organized by monitor number, starting at 0 and increasing to 9.
monitor:
0:
# Within each monitor is where you can define a your individual modules.
# You can define as many modules as you want. Some fields are required;
# these will be indicated. If a field doesn't say its required, you can
# safely omit it in your own configuration. A "bare minimum" configuration
# is shown below for your convience.
modules:
# The top field is the name of the module. This can be whatever you
# want, it doesn't matter. Because this is an example module, we will
# call it 'example.'
#example:
# # This command is run by `sh -c`, and whatever is sent to stdout is the
# # text that will be displayed for this module. `date` here is used as an
# # example. THIS IS A REQUIRED FIELD.
# command: "date"
# # How often (in milliseconds) to re-run the command and display the
# # updated text. If this value is 0, then the module will not be
# # refreshed. THIS IS A REQUIRED FIELD.
# refresh: 1000
# # Prefix to be printed before the text of the command.
# prefix: "DATE: "
# # Linux real-time signal that, when received, will refresh the module
# # instantly. See the README for more info.
# signal: 0
# # Formatting options. These will only affect the text and padding of this
# # module.
# format:
# # Where the text should be placed on the bar. `left`, `right`, or
# # `center`. THIS IS A REQUIRED FIELD.
# align: left
# # Background color of the bar.
# bg_color: "#000"
# # Text color
# fg_color: "#FFF"
# # INDEX of the font for this module to be displayed in (see above).
# font: 1
# # Horizontal offset of the module text in pixels (can be negative).
# offset: 0
# # Set an underline or overline
# line:
# # Self-explanatory. `underline` or `overline`. IF you set a line, then
# # this is a required field.
# type: underline
# # Set a custom color for the line.
# color: "#000"
# # Make this module act like a button. Both of these fields are required
# # if you are making a button. You may also define multiple buttons with
# # different activators for a single module.
# button:
# # What mouse click you have to make to activate the button. Can be
# # `left`, `center`, or `right`. You can also set it to activate when
# # you scroll up or down with `scrup` and `scrdown.`
# activator: left
# # Command to be executed upon activation. Will be run with `sh -c`.
# command: "echo 'hello' > ~/file.txt"
# Here is where you can define a list your individual modules. You can define # Here is a simpler example to more clearly show only the required fields.
# as many modules as you want. Some fields are required; these will be bare_minimum:
# indicated. If a field doesn't say its required, you can safely omit it in command: "whoami"
# your own configuration. A "bare minimum" configuration is shown below for refresh: 1000
# your convience. format:
modules: align: right
# This is an example module. You can name it whatever you want, it doesn't
# matter. Notice the `-` before the name. Each module must be a list item!
# THIS IS A REQUIRED FIELD.
#- name: "time"
# # This command is run by `sh -c`, and whatever is sent to stdout is the
# # text that will be displayed for this module. `date` here is used as an
# # example. THIS IS A REQUIRED FIELD.
# command: "date"
# # How often (in milliseconds) to re-run the command and display the
# # updated text. If this value is 0, then the module will not be
# # refreshed. THIS IS A REQUIRED FIELD.
# refresh: 1000
# # Prefix to be printed before the text of the command.
# prefix: "DATE: "
# # Linux real-time signal that, when received, will refresh the module
# # instantly. See the README for more info.
# signal: 0
# # Formatting options. These will only affect the text and padding of this
# # module.
# format:
# # Where the text should be placed on the bar. `left`, `right`, or
# # `center`. THIS IS A REQUIRED FIELD.
# align: left
# # Background color of the bar.
# bg_color: "#000"
# # Text color
# fg_color: "#FFF"
# # INDEX of the font for this module to be displayed in (see above).
# font: 1
# # Horizontal offset of the module text in pixels (can be negative).
# offset: 0
# # Set an underline or overline
# line:
# # Self-explanatory. `underline` or `overline`. IF you set a line, then
# # this is a required field.
# type: underline
# # Set a custom color for the line.
# color: "#000"
# # Make this module act like a button. Both of these fields are required
# # if you are making a button. You may also define multiple buttons with
# # different activators for a single module.
# button:
# # What mouse click you have to make to activate the button. Can be
# # `left`, `center`, or `right`. You can also set it to activate when
# # you scroll up or down with `scrup` and `scrdown.`
# activator: left
# # Command to be executed upon activation. Will be run with `sh -c`.
# command: "echo 'hello' > ~/file.txt"
# Here is a simpler example to more clearly show only the required fields.
- name: "bare_minimum"
command: "whoami"
refresh: 1000
format:
align: right

View file

@ -1,20 +1,20 @@
class Bar: class Bar:
def __init__(self, seperator, margin): def __init__(self, seperator, margin):
self.left = [] self.monitors = {}
self.center = []
self.right = []
self.seperator = seperator self.seperator = seperator
self.margin = margin self.margin = margin
def add_module(self, module): def add_monitor(self, monitor):
self.monitors[monitor] = { "left": [], "center": [], "right": [] }
def add_module(self, monitor, module):
match module.alignment: match module.alignment:
case "left": case "left":
self.left.append(module) self.monitors[monitor]["left"].append(module)
case "center": case "center":
self.center.append(module) self.monitors[monitor]["center"].append(module)
case "right": case "right":
self.right.append(module) self.monitors[monitor]["right"].append(module)
def __print_alignment_group(self, alignment_group): def __print_alignment_group(self, alignment_group):
for index, module in enumerate(alignment_group): for index, module in enumerate(alignment_group):
@ -26,13 +26,17 @@ class Bar:
break break
def print(self): def print(self):
print("%{l}" + self.margin, end='') for monitor in self.monitors:
self.__print_alignment_group(self.left) print("%{S" + str(monitor) + "}", end='')
print("%{l}" + self.margin, end='')
self.__print_alignment_group(self.monitors[monitor]["left"])
print("%{c}", end='') print("%{c}", end='')
self.__print_alignment_group(self.center) self.__print_alignment_group(self.monitors[monitor]["center"])
print("%{r}", end='') print("%{r}", end='')
self.__print_alignment_group(self.right) self.__print_alignment_group(self.monitors[monitor]["right"])
print(self.margin, flush=True) print(self.margin, end='')
print(flush=True)

View file

@ -6,8 +6,9 @@ class SignalRouter:
self.signal_thread_dictionary = {} self.signal_thread_dictionary = {}
def __route_signal(self, signal, frame): def __route_signal(self, signal, frame):
pthread_kill(self.signal_thread_dictionary[signal], signal) for thread_ident in self.signal_thread_dictionary[signal]:
pthread_kill(thread_ident, signal)
def register_signal_thread(self, signal, thread_ident): def register_signal_thread(self, signal, thread_ident):
self.signal_thread_dictionary[signal] = thread_ident self.signal_thread_dictionary.setdefault(signal, []).append(thread_ident)
sigsignal(signal, self.__route_signal) sigsignal(signal, self.__route_signal)

View file

@ -1,7 +1,8 @@
from parse_config_file import get_bar_config_and_module_config_list
from os import getenv from os import getenv
from os.path import exists from os.path import exists
from easyconf_lemonbar.parse_config_file import get_bar_config_and_monitor_list
def generate_flags(bar_config): def generate_flags(bar_config):
flags = "" flags = ""
for option in bar_config: for option in bar_config:
@ -33,6 +34,6 @@ if exists("./data/testing_config.yml"):
else: else:
config_file = str(getenv("HOME")) + "/.config/easyconf-lemonbar.yml" config_file = str(getenv("HOME")) + "/.config/easyconf-lemonbar.yml"
bar_config, module_config_list = get_bar_config_and_module_config_list(config_file) bar_config, monitor_list = get_bar_config_and_monitor_list(config_file)
print(generate_flags(bar_config)) print(generate_flags(bar_config))

View file

@ -5,7 +5,7 @@ from os.path import exists
from SignalRouter import SignalRouter from SignalRouter import SignalRouter
from Bar import Bar from Bar import Bar
from Module import Module from Module import Module
from parse_config_file import get_bar_config_and_module_config_list from parse_config_file import get_bar_config_and_monitor_list
def sigterm_handler(signal, frame): def sigterm_handler(signal, frame):
delete_pidfile(pidfile_name()) delete_pidfile(pidfile_name())
@ -17,7 +17,7 @@ def main():
else: else:
config_file = str(getenv("HOME")) + "/.config/easyconf-lemonbar.yml" config_file = str(getenv("HOME")) + "/.config/easyconf-lemonbar.yml"
bar_config, module_config_list = get_bar_config_and_module_config_list(config_file) bar_config, monitor_list = get_bar_config_and_monitor_list(config_file)
padding = bar_config["padding"] padding = bar_config["padding"]
seperator = bar_config["seperator"] seperator = bar_config["seperator"]
margin = bar_config["margin"] margin = bar_config["margin"]
@ -26,12 +26,15 @@ def main():
bar = Bar(seperator, margin) bar = Bar(seperator, margin)
for module_config in module_config_list: for monitor in monitor_list:
module = Module(module_config, padding, bar) bar.add_monitor(monitor)
bar.add_module(module) module_config_list = monitor_list[monitor]["modules"]
module.start_thread() for module_config in module_config_list.values():
if module.signal != 0: module = Module(module_config, padding, bar)
signal_router.register_signal_thread(module.signal, module.ident) bar.add_module(monitor, module)
module.start_thread()
if module.signal != 0:
signal_router.register_signal_thread(module.signal, module.ident)
create_pidfile(pidfile_name()) create_pidfile(pidfile_name())

View file

@ -2,7 +2,7 @@ from yaml import safe_load
from schema import And, Optional, Regex, Schema, SchemaError, Or from schema import And, Optional, Regex, Schema, SchemaError, Or
from signal import SIGRTMIN, SIGRTMAX from signal import SIGRTMIN, SIGRTMAX
def get_bar_config_and_module_config_list(filename): def get_bar_config_and_monitor_list(filename):
config_schema = Schema({ config_schema = Schema({
"bar": { "bar": {
Optional("config"): { Optional("config"): {
@ -20,32 +20,35 @@ def get_bar_config_and_module_config_list(filename):
Optional("seperator", default="|"): str, Optional("seperator", default="|"): str,
Optional("margin", default=""): str Optional("margin", default=""): str
}, },
"modules": [ "monitor": {
{ And(int, lambda n: 0 <= n <= 9): {
"name": str, "modules": {
"command": str, str: {
"refresh": And(int, lambda n : n >= 0), "command": str,
Optional("prefix"): str, "refresh": And(int, lambda n : n >= 0),
Optional("signal"): And(int, lambda n : SIGRTMIN <= SIGRTMIN+n <= SIGRTMAX), Optional("prefix"): str,
"format": { Optional("signal"): And(int, lambda n : SIGRTMIN <= SIGRTMIN+n <= SIGRTMAX),
"align": Or("left", "center", "right"), "format": {
Optional("bg_color"): Regex(r'^#[0-9a-fA-F]{3}$|^#[0-9a-fA-F]{6}$|^#[0-9a-fA-F]{8}$'), "align": Or("left", "center", "right"),
Optional("fg_color"): Regex(r'^#[0-9a-fA-F]{3}$|^#[0-9a-fA-F]{6}$|^#[0-9a-fA-F]{8}$'), Optional("bg_color"): Regex(r'^#[0-9a-fA-F]{3}$|^#[0-9a-fA-F]{6}$|^#[0-9a-fA-F]{8}$'),
Optional("font"): And(int, lambda n: 1 <= n <= 5), Optional("fg_color"): Regex(r'^#[0-9a-fA-F]{3}$|^#[0-9a-fA-F]{6}$|^#[0-9a-fA-F]{8}$'),
Optional("offset"): int, Optional("font"): And(int, lambda n: 1 <= n <= 5),
Optional("line"): { Optional("offset"): int,
"type": Or("underline", "overline"), Optional("line"): {
Optional("color"): Regex(r'^#[0-9a-fA-F]{3}$|^#[0-9a-fA-F]{6}$|^#[0-9a-fA-F]{8}$') "type": Or("underline", "overline"),
Optional("color"): Regex(r'^#[0-9a-fA-F]{3}$|^#[0-9a-fA-F]{6}$|^#[0-9a-fA-F]{8}$')
}
# Not yet implemented.
#Optional("button"): {
# "activator": Or("left", "center", "right", "scrup", "scrdown"),
# "command": str
#}
}
} }
# Not yet implemented.
#Optional("button"): {
# "activator": Or("left", "center", "right", "scrup", "scrdown"),
# "command": str
#}
} }
} }
] }
} }
}) })
@ -54,6 +57,6 @@ def get_bar_config_and_module_config_list(filename):
try: try:
validated_config = config_schema.validate(config_file) validated_config = config_schema.validate(config_file)
return validated_config["bar"]["config"], validated_config["bar"]["modules"] return validated_config["bar"]["config"], validated_config["bar"]["monitor"]
except SchemaError as se: except SchemaError as se:
raise se raise se