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.
|
"""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
|
||||||
|
|
Loading…
Reference in a new issue