wlrotation: refactored the entire module and integrated external services

This commit is contained in:
Ludwig Behm 2023-11-10 19:44:33 +01:00
parent bbc26c263c
commit cbd0c58b4a
Signed by: l.behm
GPG key ID: D344835D63B89384

View file

@ -3,6 +3,10 @@
"""Shows a widget for each connected screen and allows the user to loop through different orientations. """Shows a widget for each connected screen and allows the user to loop through different orientations.
Parameters:
* wlrotation.display : Name of the output display that will be rotated
+ wlrotation.auto : Boolean value if the display should be rotatet automatic by default
Requires the following executable: Requires the following executable:
* swaymsg * swaymsg
""" """
@ -11,53 +15,112 @@ import core.module
import core.input import core.input
import util.cli import util.cli
import iio
import json import json
from math import degrees, atan2, sqrt
from os import environ, path from os import environ, path
possible_orientations = ["normal", "90", "180", "270"] possible_orientations = ["normal", "90", "180", "270"]
class iioValue:
def __init__(self, channel):
self.channel = channel
self.scale = self.read('scale')
self.offset = self.read('offset')
def read(self, attr):
return float(self.channel.attrs[attr].value)
def value(self):
return (self.read('raw') + self.offset) * self.scale
class iioAccelDevice:
def __init__(self):
self.ctx = iio.Context() # store ctx pointer
d = self.ctx.find_device('accel_3d')
self.x = iioValue(d.find_channel('accel_x'))
self.y = iioValue(d.find_channel('accel_y'))
self.z = iioValue(d.find_channel('accel_z'))
def orientation(self):
"""
returns tuple of `[success, value]` where `success` indicates, if an accurate value could be meassured and `value` the sway output api compatible value or `normal` if success is `False`
"""
x_deg, y_deg, z_deg = self._deg()
if abs(z_deg) < 70: # checks if device is angled too shallow
if x_deg >= 70: return True, "270"
if x_deg <= -70: return True, "90"
if abs(x_deg) <= 20:
if y_deg < 0: return True, "normal"
if y_deg > 0: return True, "180"
return False, "normal"
def _deg(self):
gravity = 9.81
x, y, z = self.x.value() / gravity, self.y.value() / gravity, self.z.value() / gravity
return degrees(atan2(x, sqrt(pow(y, 2) + pow(z, 2)))), degrees(atan2(y, sqrt(pow(z, 2) + pow(x, 2)))), degrees(atan2(z, sqrt(pow(x, 2) + pow(y, 2))))
class Display():
def __init__(self, name, widget, display_data, auto=False):
self.name = name
self.widget = widget
self.accelDevice = iioAccelDevice()
self._lock_auto_rotation(not auto)
self.widget.set("orientation", display_data['transform'])
core.input.register(widget, button=core.input.LEFT_MOUSE, cmd=self.rotate_90deg)
core.input.register(widget, button=core.input.RIGHT_MOUSE, cmd=self.toggle)
def rotate_90deg(self, event):
# compute new orientation based on current orientation
current = self.widget.get("orientation")
self._set_rotation(possible_orientations[(possible_orientations.index(current) + 1) % len(possible_orientations)])
# disable auto rotation
self._lock_auto_rotation(True)
def toggle(self, event):
self._lock_auto_rotation(not self.locked)
def auto_rotate(self):
# automagically rotate the display based on sensor values
# this is only called if rotation lock is disabled
success, value = self.accelDevice.orientation()
if success:
self._set_rotation(value)
def _set_rotation(self, new_orientation):
self.widget.set("orientation", new_orientation)
util.cli.execute("swaymsg 'output {} transform {}'".format(self.name, new_orientation))
def _lock_auto_rotation(self, locked):
self.locked = locked
self.widget.set("locked", self.locked)
class Module(core.module.Module): class Module(core.module.Module):
@core.decorators.every(seconds=1)
def __init__(self, config, theme): def __init__(self, config, theme):
super().__init__(config, theme, []) super().__init__(config, theme, [])
self.service = "sway-auto-rotate@%s" % path.basename(environ['SWAYSOCK']).replace('-', '\\\\x2d') self.display = None
# self._widgets = [] display_filter = self.parameter("display", None)
self.update_widgets() for display in json.loads(util.cli.execute("swaymsg -t get_outputs -r")):
def update_widgets(self):
for display in json.loads(util.cli.execute("/usr/bin/swaymsg -t get_outputs -r")):
name = display['name'] name = display['name']
widget = self.widget(name) if display_filter == None or display_filter == name:
if not widget: self.display = Display(name, self.add_widget(name=name), display, auto=util.format.asbool(self.parameter("auto", False)))
widget = self.add_widget(name=name, full_text="󰑵 "+name) break # I assume that it makes only sense to rotate a single screen
widget.set("auto", util.cli.execute("systemctl --user show %s -p ActiveState" % self.service).split("\n")[0].split("=")[1])
core.input.register(widget, button=core.input.LEFT_MOUSE, cmd=self._toggle) def update(self):
core.input.register(widget, button=core.input.RIGHT_MOUSE, cmd=self._toggle_auto) if self.display == None:
widget.set("orientation", display['transform']) return
if self.display.locked:
return
self.display.auto_rotate()
def state(self, widget): def state(self, widget):
curr = widget.get("auto", "inactive") state = []
if curr == "inactive": state.append("locked" if widget.get("locked", True) else "auto")
return ["warning"] return state
return [curr]
def _toggle_auto(self, event):
widget = self.widget(widget_id=event["instance"])
if widget.get("auto") == "inactive":
util.cli.execute("systemctl --user start %s" % self.service)
else:
util.cli.execute("systemctl --user stop %s" % self.service)
widget.set("auto", util.cli.execute("systemctl --user show %s -p ActiveState" % self.service).split("\n")[0].split("=")[1])
def _toggle(self, event):
widget = self.widget(widget_id=event["instance"])
# compute new orientation based on current orientation
idx = (possible_orientations.index(widget.get("orientation"))+ 1) % len(possible_orientations)
new_orientation = possible_orientations[idx]
widget.set("orientation", new_orientation)
util.cli.execute("swaymsg 'output {} transform {}'".format(widget.name, new_orientation))
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4