-
Notifications
You must be signed in to change notification settings - Fork 8
/
thinkpad_x1_yoga_rotation.py
executable file
·154 lines (128 loc) · 5.88 KB
/
thinkpad_x1_yoga_rotation.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#!/usr/bin/env python3
"""
Usage:
thinkpad_x1_yoga_rotation.py [options]
Options:
-h,--help display help message
--version display version and exit
"""
import dbus, sys, time, subprocess, socket, logging, docopt, multiprocessing, io, os, signal, atexit
from gi.repository import GLib
from dbus.mainloop.glib import DBusGMainLoop
name = "thinkpad_x1_yoga_rotation"
version = "0.9-SNAPSHOT"
# map sensor-proxy orientation to xrandr and wacom
xrandr_orientation_map = {
'right-up': 'right',
'normal' : 'normal',
'bottom-up': 'inverted',
'left-up': 'left'
}
wacom_orientation_map = {
'right-up': 'cw',
'normal' : 'none',
'bottom-up': 'half',
'left-up': 'ccw'
}
def cmd_and_log(cmd):
exit = subprocess.call(cmd)
log.info("running %s with exit code %s", cmd, exit)
def sensor_proxy_signal_handler(source, changedProperties, invalidatedProperties, **kwargs):
if source=='net.hadess.SensorProxy':
if 'AccelerometerOrientation' in changedProperties:
orientation = changedProperties['AccelerometerOrientation']
log.info("dbus signal indicates orientation change to %s", orientation)
subprocess.call(["xrandr", "-o", xrandr_orientation_map[orientation]])
for device in wacom:
cmd_and_log(["xsetwacom", "--set", device, "rotate", wacom_orientation_map[orientation]])
# toggle trackpoint and touchpad when changing from laptop to tablet mode anc vice versa
def monitor_acpi_events(touch_and_track):
socketACPI = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
socketACPI.connect("/var/run/acpid.socket")
is_laptop_mode = True
log.info("connected to acpi socket %s", socket)
onboard_pid = None
while True:
event = socketACPI.recv(4096)
log.debug("catching acpi event %s", event)
#eventACPIDisplayPositionChange = "ibm/hotkey LEN0068:00 00000080 000060c0\n"
eventACPIDisplayPositionChange = b" PNP0C14:03 000000b0 00000000\n"
if event == eventACPIDisplayPositionChange:
is_laptop_mode = not is_laptop_mode
log.info("display position change detected, laptop mode %s", is_laptop_mode)
if is_laptop_mode:
for x in touch_and_track:
cmd_and_log(["xinput", "enable", x])
log.info("onboard pid %s", onboard_pid)
if onboard_pid:
log.info("stopping onboard")
os.kill(onboard_pid, signal.SIGTERM)
else:
for x in touch_and_track:
cmd_and_log(["xinput", "disable", x])
#subprocess.call(["xinput", "--disable", "SynPS/2 Synaptics TouchPad"])
p = subprocess.Popen(['nohup', 'onboard'],
stdout=open('/dev/null', 'w'),
#stderr=open('logfile.log', 'a'),
preexec_fn=os.setpgrp
)
onboard_pid = p.pid
log.info("started onboard with pid %s", onboard_pid)
time.sleep(0.3)
def monitor_stylus_proximity(stylus, finger_touch):
out = subprocess.Popen(["xinput", "test", "-proximity", stylus], stdout=subprocess.PIPE)
for line in out.stdout:
if (line.startswith(b'proximity')):
log.debug(line)
status = line.split(b' ')[1]
cmd_and_log(["xinput", "disable" if status==b'in' else "enable", finger_touch])
def cleanup(touch_and_track, wacom):
subprocess.call(["xrandr", "-o", "normal"])
for x in touch_and_track:
cmd_and_log(["xinput", "enable", x])
for device in wacom:
cmd_and_log(["xsetwacom", "--set", device, "rotate", "none"])
def main(options):
# logging
global log
log = logging.getLogger()
logHandler = logging.StreamHandler()
log.addHandler(logHandler)
logHandler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
log.level = logging.INFO
# load wacom devices
lines = subprocess.check_output(['xsetwacom','--list', 'devices']).split(b'\n')
global wacom
wacom = [x.decode().split('\t')[0].strip() for x in lines if x]
log.info("detected wacom devices: %s", wacom)
# load stylus touchpad trackpoint devices
lines = subprocess.check_output(['xinput','--list', '--name-only']).decode().split('\n')
stylus = next(x.strip() for x in lines if "stylus" in x)
log.info("found stylus %s", stylus)
finger_touch = next(x.strip() for x in lines if "Finger touch" in x)
log.info("found finger touch %s", finger_touch)
# it's crucial to have trackpoints first in this list. Otherwise enabling/disabling doesn't work as expected and touchpad just stays enabled always
touch_and_track = [x.strip() for x in lines if "TrackPoint" in x] + [x.strip() for x in lines if "TouchPad" in x]
log.info("found touchpad and trackpoints %s", touch_and_track)
# listen for ACPI events to detect switching between laptop/tablet mode
acpi_process = multiprocessing.Process(target = monitor_acpi_events, args=(touch_and_track,))
acpi_process.start()
proximity_process = multiprocessing.Process(target = monitor_stylus_proximity, args=(stylus, finger_touch))
proximity_process.start()
atexit.register(cleanup, touch_and_track, wacom)
# init dbus stuff and subscribe to events
DBusGMainLoop(set_as_default=True)
bus = dbus.SystemBus()
proxy = bus.get_object('net.hadess.SensorProxy', '/net/hadess/SensorProxy')
props = dbus.Interface(proxy, 'org.freedesktop.DBus.Properties')
props.connect_to_signal('PropertiesChanged', sensor_proxy_signal_handler, sender_keyword='sender')
iface = dbus.Interface(proxy, 'net.hadess.SensorProxy')
iface.ClaimAccelerometer()
loop = GLib.MainLoop()
loop.run()
if __name__ == "__main__":
options = docopt.docopt(__doc__)
if options["--version"]:
print(version)
exit()
main(options)