232 lines
6.6 KiB
Python
232 lines
6.6 KiB
Python
import re
|
|
import subprocess
|
|
import traceback
|
|
from time import sleep
|
|
from typing import List
|
|
|
|
import notify2
|
|
from dbus import DBusException
|
|
from libqtile import widget
|
|
from notify2 import Notification, URGENCY_NORMAL
|
|
from libqtile.log_utils import logger
|
|
from libqtile import qtile
|
|
|
|
BUTTON_LEFT = 1
|
|
BUTTON_MIDDLE = 2
|
|
BUTTON_RIGHT = 3
|
|
BUTTON_UP = 4
|
|
BUTTON_DOWN = 5
|
|
BUTTON_MUTE = 1
|
|
BUTTON_SCROLL_UP = 4
|
|
BUTTON_SCROLL_DOWN = 5
|
|
|
|
|
|
def is_running(process):
|
|
s = subprocess.Popen(["ps", "axuw"], stdout=subprocess.PIPE)
|
|
if isinstance(process, list):
|
|
process = "".join(process)
|
|
for x in s.stdout:
|
|
if re.search(process, x.decode('utf-8')):
|
|
return True
|
|
return False
|
|
|
|
|
|
def execute(process):
|
|
try:
|
|
if isinstance(process, list):
|
|
return subprocess.Popen(process)
|
|
elif isinstance(process, str):
|
|
return subprocess.Popen(process.split())
|
|
else:
|
|
logger.info(f"Failed to execute_once")
|
|
except FileNotFoundError as e:
|
|
logger.error(f"Could not execute {process}, FileNotFoundError - {e}")
|
|
|
|
|
|
def execute_once(process):
|
|
logger.info(f"Attempting to execute_once: {process}")
|
|
if not is_running(process):
|
|
return execute(process)
|
|
logger.info(f"Process was already running: {process}")
|
|
|
|
|
|
def start_in_group(theme, qtile, group: str, command: List[str], floating: bool = False,
|
|
intrusive: bool = False, dont_break: bool = False):
|
|
try:
|
|
proc = subprocess.Popen(command)
|
|
match_args = {"net_wm_pid": proc.pid}
|
|
rule_args = {
|
|
"float": floating,
|
|
"intrusive": intrusive,
|
|
"group": group,
|
|
"break_on_match": not dont_break,
|
|
}
|
|
rule_id = qtile.add_rule(match_args, rule_args)
|
|
theme.autostart_app_rules[proc.pid] = rule_id
|
|
return proc
|
|
except FileNotFoundError as e:
|
|
logger.error(f"Could not execute {process}, FileNotFoundError - {e}")
|
|
|
|
|
|
def start_in_group_once(theme, qtile, group: str, command: List[str], floating: bool = False,
|
|
intrusive: bool = False, dont_break: bool = False):
|
|
logger.info(f"Attempting to start_in_group_once: {command}")
|
|
if not is_running(command):
|
|
return start_in_group(theme=theme, qtile=qtile, group=group, command=command,
|
|
floating=floating, intrusive=intrusive, dont_break=dont_break)
|
|
logger.info(f"Process was already running: {command}")
|
|
|
|
|
|
def call_process(command, **kwargs):
|
|
"""
|
|
Run the given command and return the string from stdout.
|
|
"""
|
|
return subprocess.check_output(command, **kwargs).decode()
|
|
|
|
|
|
def get_screen_count():
|
|
try:
|
|
if qtile.core.name == "x11":
|
|
logger.info("Using xrandr to detect screen count")
|
|
output = subprocess.check_output("xrandr -q".split()).decode('utf-8')
|
|
output = [x for x in output.split("\n") if " connected" in x]
|
|
else:
|
|
logger.info("Using lsmon (wallutils) to detect screen count")
|
|
output = subprocess.check_output(["lsmon"]).decode('utf-8')
|
|
output = output.split("\n")
|
|
except subprocess.CalledProcessError:
|
|
return 1
|
|
|
|
if output:
|
|
return len(output)
|
|
else:
|
|
return 1
|
|
|
|
|
|
def bar_separator(config):
|
|
return widget.Sep(foreground=config.get('colour_spacer_background', '#777777'),
|
|
linewidth=config.get('width_spacer', 1),
|
|
padding=config.get('padding_spacer', 4),
|
|
)
|
|
|
|
def init_notify(qtile):
|
|
if qtile and qtile.theme_instance and qtile.theme_instance.startup_completed:
|
|
try:
|
|
if not notify2.is_initted():
|
|
logger.warning("Initializing Notify2")
|
|
notify2.init("QTileWM")
|
|
except DBusException:
|
|
logger.error(f"Failed to initialize Notify2 (DBus error), retrying later.")
|
|
except Exception:
|
|
logger.error(f"Failed to initialize Notify2 (Generic error), retrying later.")
|
|
else:
|
|
logger.warning(f"Not initializing Notify2 yet, QTile startup not completed.")
|
|
|
|
|
|
def notify(qtile, title, content, urgency=URGENCY_NORMAL, timeout=5000, image=None):
|
|
if image is not None:
|
|
notification = Notification(
|
|
summary=title, message=content,
|
|
icon=image
|
|
)
|
|
else:
|
|
notification = Notification(
|
|
summary=title, message=content
|
|
)
|
|
notification.set_timeout(timeout)
|
|
notification.set_urgency(urgency)
|
|
|
|
init_notify(qtile)
|
|
|
|
try:
|
|
try:
|
|
return notification.show()
|
|
except notify2.UninittedError:
|
|
logger.warning("Notify2 is not initialized")
|
|
except Exception as e:
|
|
logger.warning("Showing notification failed: {}".format(e))
|
|
logger.warning(traceback.format_exc())
|
|
|
|
|
|
def spawn_popup(qtile, x, y, text):
|
|
"""
|
|
:param qtile: The main qtile instance
|
|
:type qtile: Qtile
|
|
:param x: x-coordinate
|
|
:type x: int
|
|
:param y: y-coordinate
|
|
:type y: int
|
|
:param text: String to display
|
|
:type text: str
|
|
:return: The popup instance
|
|
:rtype: Internal
|
|
"""
|
|
if qtile.core.name == "x11":
|
|
from libqtile.backend.x11.window import Internal
|
|
else:
|
|
from libqtile.backend.wayland.window import Internal
|
|
popup = Internal.create(
|
|
qtile, x, y, 100, 100, opacity=1
|
|
)
|
|
|
|
# Create textwidget for in window
|
|
popup.bordercolor = "#000000"
|
|
popup.borderwidth = 1
|
|
|
|
popup.focus(False)
|
|
|
|
#popup.
|
|
|
|
return popup
|
|
|
|
|
|
def despawn_popup(popup):
|
|
"""
|
|
:type popup: Internal
|
|
:param popup: The popup to despawn
|
|
"""
|
|
popup.kill()
|
|
|
|
|
|
def test_popups(qtile):
|
|
popup = spawn_popup(qtile, 10, 10, "Hello World!")
|
|
sleep(3)
|
|
despawn_popup(popup)
|
|
|
|
|
|
def display_wm_class(qtile):
|
|
window = qtile.current_window if qtile else None
|
|
|
|
if window:
|
|
wm_class = window.get_wm_class() or None
|
|
name = window.name
|
|
|
|
if wm_class:
|
|
notify(qtile=qtile, title="WM_Class of {}".format(name),
|
|
content="{}".format(wm_class),
|
|
urgency=notify2.URGENCY_CRITICAL)
|
|
|
|
|
|
def bluetooth_audio_sink():
|
|
try:
|
|
output = subprocess.check_output("pamixer --list-sinks".split()).decode("utf-8")
|
|
output = [x for x in output.split('\n') if "blue" in x.lower()]
|
|
except (subprocess.CalledProcessError, FileNotFoundError):
|
|
return -1
|
|
|
|
sink = -1
|
|
try:
|
|
sink = int(output[0].split()[0])
|
|
except IndexError:
|
|
pass
|
|
except AttributeError:
|
|
pass
|
|
except ValueError:
|
|
pass
|
|
|
|
return sink
|
|
|
|
|
|
def bluetooth_audio_connected():
|
|
return bluetooth_audio_sink() != -1
|