wlrotation: refactored the entire module and integrated external services
This commit is contained in:
parent
bbc26c263c
commit
cbd0c58b4a
1 changed files with 99 additions and 36 deletions
|
@ -3,6 +3,10 @@
|
|||
|
||||
"""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:
|
||||
* swaymsg
|
||||
"""
|
||||
|
@ -11,53 +15,112 @@ import core.module
|
|||
import core.input
|
||||
import util.cli
|
||||
|
||||
import iio
|
||||
import json
|
||||
from math import degrees, atan2, sqrt
|
||||
from os import environ, path
|
||||
|
||||
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):
|
||||
@core.decorators.every(seconds=1)
|
||||
def __init__(self, config, theme):
|
||||
super().__init__(config, theme, [])
|
||||
|
||||
self.service = "sway-auto-rotate@%s" % path.basename(environ['SWAYSOCK']).replace('-', '\\\\x2d')
|
||||
# self._widgets = []
|
||||
self.update_widgets()
|
||||
|
||||
def update_widgets(self):
|
||||
for display in json.loads(util.cli.execute("/usr/bin/swaymsg -t get_outputs -r")):
|
||||
self.display = None
|
||||
display_filter = self.parameter("display", None)
|
||||
for display in json.loads(util.cli.execute("swaymsg -t get_outputs -r")):
|
||||
name = display['name']
|
||||
widget = self.widget(name)
|
||||
if not widget:
|
||||
widget = self.add_widget(name=name, full_text=" "+name)
|
||||
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)
|
||||
core.input.register(widget, button=core.input.RIGHT_MOUSE, cmd=self._toggle_auto)
|
||||
widget.set("orientation", display['transform'])
|
||||
if display_filter == None or display_filter == name:
|
||||
self.display = Display(name, self.add_widget(name=name), display, auto=util.format.asbool(self.parameter("auto", False)))
|
||||
break # I assume that it makes only sense to rotate a single screen
|
||||
|
||||
def update(self):
|
||||
if self.display == None:
|
||||
return
|
||||
if self.display.locked:
|
||||
return
|
||||
|
||||
self.display.auto_rotate()
|
||||
|
||||
def state(self, widget):
|
||||
curr = widget.get("auto", "inactive")
|
||||
if curr == "inactive":
|
||||
return ["warning"]
|
||||
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))
|
||||
state = []
|
||||
state.append("locked" if widget.get("locked", True) else "auto")
|
||||
return state
|
||||
|
||||
# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4
|
||||
|
|
Loading…
Reference in a new issue