You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							209 lines
						
					
					
						
							6.8 KiB
						
					
					
				
			
		
		
	
	
							209 lines
						
					
					
						
							6.8 KiB
						
					
					
				#!/usr/bin/env python3 | 
						|
 | 
						|
import subprocess | 
						|
 | 
						|
from PyQt5 import QtCore, QtDBus, QtWidgets | 
						|
from PyQt5.QtCore import pyqtSignal as Signal, pyqtSlot as Slot, pyqtProperty as Property, Q_CLASSINFO | 
						|
 | 
						|
 | 
						|
KSCREEN_OUTPUT = "DSI-1" | 
						|
XINPUT_TOUCH = "silead_ts" | 
						|
 | 
						|
 | 
						|
IIO_BUSNAME = "net.hadess.SensorProxy" | 
						|
IIO_OBJPATH = "/net/hadess/SensorProxy" | 
						|
 | 
						|
IIO_TO_KSCREEN = { | 
						|
    "normal": "none", | 
						|
    "right-up": "right", | 
						|
    "left-up": "left", | 
						|
    "bottom-up": "inverted" | 
						|
} | 
						|
 | 
						|
 | 
						|
# DBus | 
						|
class DISensorProxy(QtDBus.QDBusAbstractInterface): | 
						|
    HasAccelerometerChanged = Signal(bool) | 
						|
    @Property(bool, notify=HasAccelerometerChanged) | 
						|
    def HasAccelerometer(self): | 
						|
        return self.property("HasAccelerometer") | 
						|
     | 
						|
    AccelerometerOrientationChanged = Signal(str) | 
						|
    @Property(str, notify=AccelerometerOrientationChanged) | 
						|
    def AccelerometerOrientation(self) -> str: | 
						|
        return self.property("AccelerometerOrientation") | 
						|
     | 
						|
     | 
						|
    def ClaimAccelerometer(self): | 
						|
        self.call("ClaimAccelerometer") | 
						|
     | 
						|
    def ReleaseAccelerometer(self): | 
						|
        self.call("ReleaseAccelerometer") | 
						|
 | 
						|
 | 
						|
    @Slot(QtDBus.QDBusMessage) | 
						|
    def _on_properties_changed(self, msg): | 
						|
        intf, updated, invald = msg.arguments() | 
						|
        updated = dict(updated) | 
						|
         | 
						|
        if "HasAccelerometer" in updated: | 
						|
            self.HasAccelerometerChanged.emit(updated["HasAccelerometerChanged"]) | 
						|
        if "AccelerometerOrientation" in updated: | 
						|
            self.AccelerometerOrientationChanged.emit(updated["AccelerometerOrientation"]) | 
						|
     | 
						|
    def __init__(self, service, path, connection, parent=None): | 
						|
        super().__init__(service, path, "net.hadess.SensorProxy", connection, parent) | 
						|
        if not connection.connect(service, path, "org.freedesktop.DBus.Properties", | 
						|
                           "PropertiesChanged", ["net.hadess.SensorProxy"], "sa{sv}as", | 
						|
                           self._on_properties_changed): | 
						|
            raise RuntimeError("Could not connect to PropertiesChanged") | 
						|
 | 
						|
 | 
						|
class DARotated(QtDBus.QDBusAbstractAdaptor): | 
						|
    Q_CLASSINFO("D-Bus Interface", "me.sodimm.oro.Rotated") | 
						|
     | 
						|
    def __init__(self, app): | 
						|
        super().__init__(app) | 
						|
        self.app = app | 
						|
         | 
						|
        app.orientationChanged.connect(self.ScreenTurned) | 
						|
     | 
						|
    ScreenTurned = Signal(str) | 
						|
     | 
						|
    @Property(str) | 
						|
    def Orientation(self): | 
						|
        return self.app.orientation | 
						|
     | 
						|
    @Property(bool) | 
						|
    def AutoTurn(self): | 
						|
        return self.app.turn_enabled | 
						|
     | 
						|
    @AutoTurn.write | 
						|
    def AutoTurn(self, val): | 
						|
        self.app.set_turn_enabled(val) | 
						|
     | 
						|
    @Slot(str, result=bool) | 
						|
    def Turn(self, orientation): | 
						|
        if orientation in ("none", "right", "left", "inverted"): | 
						|
            self.app.turn_screen(orientation) | 
						|
            return True | 
						|
        return False | 
						|
     | 
						|
    @Slot() | 
						|
    def Quit(self): | 
						|
        self.app.qapp.quit() | 
						|
 | 
						|
 | 
						|
# App Logic | 
						|
class Main(QtCore.QObject): | 
						|
    def __init__(self, argv): | 
						|
        super().__init__() | 
						|
        #self.qapp = QtCore.QCoreApplication(argv) | 
						|
        self.qapp = QtWidgets.QApplication(argv) | 
						|
        self.qapp.setApplicationName("iio-rotated") | 
						|
        self.qapp.setApplicationDisplayName("Screen Rotation") | 
						|
         | 
						|
        # TODO: Figure out initial orientation | 
						|
        self.orientation = None | 
						|
        self.turn_enabled = True | 
						|
 | 
						|
    # Turning | 
						|
    orientationChanged = Signal(str) | 
						|
 | 
						|
    def turn_screen(self, orientation): | 
						|
        """ | 
						|
        Turn the Display into a different orientation | 
						|
        @param orientation The KScreen orientation (none|right|left|inverted) | 
						|
        """ | 
						|
        subprocess.check_call(["kscreen-doctor", "output.%s.rotation.%s" % (KSCREEN_OUTPUT, orientation)]) | 
						|
        subprocess.check_call(["xinput", "--map-to-output", XINPUT_TOUCH, KSCREEN_OUTPUT]) | 
						|
        self.orientation = orientation | 
						|
        self.orientationChanged.emit(orientation) | 
						|
 | 
						|
    # Auto-Turning | 
						|
    def on_device_turned(self, direction): | 
						|
        print("Device Turned", direction) | 
						|
        if self.turn_enabled: | 
						|
            self.turn_screen(IIO_TO_KSCREEN[direction]) | 
						|
     | 
						|
    def set_turn_enabled(self, v): | 
						|
        self.turn_enabled = v | 
						|
        self.turn_enabled_changed.emit(v) | 
						|
     | 
						|
    turn_enabled_changed = Signal(bool) | 
						|
     | 
						|
    def on_systray_clicked(self, reason): | 
						|
        print("Activated", reason) | 
						|
        # TODO: this should change the Icon | 
						|
        #if reason == QtWidgets.QSystemTrayIcon.Trigger: | 
						|
        #    self.set_turn_enabled(not self.turn_enabled) | 
						|
         | 
						|
    # Main | 
						|
    def main(self): | 
						|
        # Set up Session Bus | 
						|
        self.session_bus = QtDBus.QDBusConnection.sessionBus() | 
						|
         | 
						|
        if not self.session_bus.isConnected(): | 
						|
            raise RuntimeError("Not connected to Session Bus") | 
						|
         | 
						|
        if not self.session_bus.registerService("me.sodimm.oro.Rotated"): | 
						|
            raise RuntimeError("Could not register D-Bus Service. Maybe another instance is already running") | 
						|
         | 
						|
        self.adaptor = DARotated(self) | 
						|
        self.session_bus.registerObject("/Rotated", self) | 
						|
     | 
						|
        # Connect to System Bus | 
						|
        self.system_bus = QtDBus.QDBusConnection.systemBus() | 
						|
 | 
						|
        if not self.system_bus.isConnected(): | 
						|
            raise RuntimeError("Not connected to System Bus") | 
						|
     | 
						|
        # Look for iio-sensor-proxy | 
						|
        iio = DISensorProxy(IIO_BUSNAME, IIO_OBJPATH, self.system_bus) | 
						|
 | 
						|
        if not iio.HasAccelerometer: | 
						|
            raise RuntimeError("No accelerometer reported") | 
						|
  | 
						|
        # Set up System Tray Icon | 
						|
        # TODO: Directly work with StatusNotifierItem API? | 
						|
        self.systray = QtWidgets.QSystemTrayIcon(self) | 
						|
        #self.systray.setToolTip("Screen Orientation") | 
						|
 | 
						|
        self.menu = QtWidgets.QMenu() | 
						|
        action = self.menu.addAction("Auto-Turn") | 
						|
        action.setCheckable(True) | 
						|
        action.setChecked(self.turn_enabled) | 
						|
        self.turn_enabled_changed.connect(lambda v, action=action: action.setChecked(v)) | 
						|
        action.triggered.connect(lambda c: self.set_turn_enabled(c)) | 
						|
        self.menu.addSeparator() | 
						|
        for label, direction in (("Normal", "none"), | 
						|
                                 ("Anti-CW", "right"), | 
						|
                                 ("Clockwise", "left"), | 
						|
                                 ("Upside-Down", "inverted")): | 
						|
            action = self.menu.addAction("Turn %s" % label) | 
						|
            action.triggered.connect(lambda *a, d=direction: self.turn_screen(d)) | 
						|
            action.setEnabled(not self.turn_enabled) | 
						|
            self.turn_enabled_changed.connect(lambda v, action=action: action.setEnabled(not v)) | 
						|
        self.menu.addSeparator() | 
						|
        action = self.menu.addAction("Quit") | 
						|
        action.triggered.connect(self.qapp.quit) | 
						|
        self.systray.setContextMenu(self.menu) | 
						|
         | 
						|
        self.systray.activated.connect(self.on_systray_clicked) | 
						|
        self.systray.show() | 
						|
     | 
						|
        # Run | 
						|
        iio.ClaimAccelerometer() | 
						|
     | 
						|
        iio.AccelerometerOrientationChanged.connect(self.on_device_turned) | 
						|
     | 
						|
        try: | 
						|
            self.qapp.exec() | 
						|
        finally: | 
						|
            iio.ReleaseAccelerometer() | 
						|
     | 
						|
 | 
						|
 | 
						|
if __name__ == "__main__": | 
						|
    import sys | 
						|
    Main(sys.argv).main()
 | 
						|
 |