|
#!/usr/bin/python
|
|
|
|
# Script to automatically update Raspberry Pi PiTFT touchscreen calibration
|
|
# based on the current rotation of the screen.
|
|
|
|
# Copyright (c) 2014 Adafruit Industries
|
|
# Author: Tony DiCola
|
|
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
|
|
# The above copyright notice and this permission notice shall be included in all
|
|
# copies or substantial portions of the Software.
|
|
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
# SOFTWARE.
|
|
import argparse
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
# Calibration configuration default values.
|
|
CAL_CONFIG = {}
|
|
|
|
# 2.8" resisitive touch calibration values.
|
|
CAL_CONFIG['28r'] = {}
|
|
CAL_CONFIG['28r']['pointercal'] = {}
|
|
CAL_CONFIG['28r']['pointercal']['0'] = '4315 -49 -889068 18 5873 -1043172 6553636'
|
|
CAL_CONFIG['28r']['pointercal']['90'] = '-30 -5902 22077792 4360 -105 -1038814 65536'
|
|
CAL_CONFIG['28r']['pointercal']['180'] = '-4228 73 16353030 -60 -5888 22004262 65536'
|
|
CAL_CONFIG['28r']['pointercal']['270'] = '-69 5859 -829540 -4306 3 16564590 6553636'
|
|
CAL_CONFIG['28r']['xorg'] = {}
|
|
CAL_CONFIG['28r']['xorg']['0'] = """
|
|
Section "InputClass"
|
|
Identifier "calibration"
|
|
MatchProduct "stmpe-ts"
|
|
Option "Calibration" "252 3861 180 3745"
|
|
Option "SwapAxes" "0"
|
|
EndSection
|
|
"""
|
|
CAL_CONFIG['28r']['xorg']['90'] = """
|
|
Section "InputClass"
|
|
Identifier "calibration"
|
|
MatchProduct "stmpe-ts"
|
|
Option "Calibration" "3807 174 244 3872"
|
|
Option "SwapAxes" "1"
|
|
EndSection
|
|
"""
|
|
CAL_CONFIG['28r']['xorg']['180'] = """
|
|
Section "InputClass"
|
|
Identifier "calibration"
|
|
MatchProduct "stmpe-ts"
|
|
Option "Calibration" "3868 264 3789 237"
|
|
Option "SwapAxes" "0"
|
|
EndSection
|
|
"""
|
|
CAL_CONFIG['28r']['xorg']['270'] = """
|
|
Section "InputClass"
|
|
Identifier "calibration"
|
|
MatchProduct "stmpe-ts"
|
|
Option "Calibration" "287 3739 3817 207"
|
|
Option "SwapAxes" "1"
|
|
EndSection
|
|
"""
|
|
|
|
# 2.8" capacitive touch calibration values.
|
|
CAL_CONFIG['28c'] = {}
|
|
CAL_CONFIG['28c']['pointercal'] = {}
|
|
CAL_CONFIG['28c']['pointercal']['0'] = '-65536 0 15728640 -320 -65536 20971520 65536'
|
|
CAL_CONFIG['28c']['pointercal']['90'] = '320 65536 0 -65536 0 15728640 65536'
|
|
CAL_CONFIG['28c']['pointercal']['180'] = '65536 0 -655360 0 65536 -655360 65536'
|
|
CAL_CONFIG['28c']['pointercal']['270'] = '0 -65536 20971520 65536 0 -65536 65536'
|
|
CAL_CONFIG['28c']['xorg'] = {}
|
|
CAL_CONFIG['28c']['xorg']['0'] = """
|
|
Section "InputClass"
|
|
Identifier "captouch"
|
|
MatchProduct "ft6x06_ts"
|
|
Option "SwapAxes" "0"
|
|
Option "InvertY" "1"
|
|
Option "InvertX" "1"
|
|
Option "Calibration" "0 240 0 320"
|
|
EndSection
|
|
"""
|
|
CAL_CONFIG['28c']['xorg']['90'] = """
|
|
Section "InputClass"
|
|
Identifier "captouch"
|
|
MatchProduct "ft6x06_ts"
|
|
Option "SwapAxes" "1"
|
|
Option "InvertY" "1"
|
|
Option "Calibration" "0 320 0 240"
|
|
EndSection
|
|
"""
|
|
CAL_CONFIG['28c']['xorg']['180'] = """
|
|
Section "InputClass"
|
|
Identifier "captouch"
|
|
MatchProduct "ft6x06_ts"
|
|
Option "SwapAxes" "0"
|
|
Option "InvertY" "0"
|
|
Option "Calibration" "0 240 0 320"
|
|
EndSection
|
|
"""
|
|
CAL_CONFIG['28c']['xorg']['270'] = """
|
|
Section "InputClass"
|
|
Identifier "captouch"
|
|
MatchProduct "ft6x06_ts"
|
|
Option "SwapAxes" "1"
|
|
Option "InvertY" "0"
|
|
Option "InvertX" "1"
|
|
Option "Calibration" "0 320 0 240"
|
|
EndSection
|
|
"""
|
|
|
|
# 3.5" resisitive touch calibration values.
|
|
CAL_CONFIG['35r'] = {}
|
|
CAL_CONFIG['35r']['pointercal'] = {}
|
|
CAL_CONFIG['35r']['pointercal']['0'] = '5835 56 -1810410 22 8426 -1062652 65536'
|
|
CAL_CONFIG['35r']['pointercal']['90'] = '-16 -8501 33169914 5735 45 -1425640 65536'
|
|
CAL_CONFIG['35r']['pointercal']['180'] = '-5853 8 22390770 -59 -8353 32810368 65536'
|
|
CAL_CONFIG['35r']['pointercal']['270'] = '-95 8395 -908648 -5849 164 22156762 65536'
|
|
CAL_CONFIG['35r']['xorg'] = {}
|
|
CAL_CONFIG['35r']['xorg']['0'] = """
|
|
Section "InputClass"
|
|
Identifier "calibration"
|
|
MatchProduct "stmpe-ts"
|
|
Option "Calibration" "291 3847 141 3889"
|
|
Option "SwapAxes" "0"
|
|
EndSection
|
|
"""
|
|
CAL_CONFIG['35r']['xorg']['90'] = """
|
|
Section "InputClass"
|
|
Identifier "calibration"
|
|
MatchProduct "stmpe-ts"
|
|
Option "Calibration" "150 3912 3843 255"
|
|
Option "SwapAxes" "1"
|
|
Option "InvertX" "1"
|
|
Option "InvertY" "1"
|
|
EndSection
|
|
"""
|
|
CAL_CONFIG['35r']['xorg']['180'] = """
|
|
Section "InputClass"
|
|
Identifier "calibration"
|
|
MatchProduct "stmpe-ts"
|
|
Option "Calibration" "291 3847 141 3889"
|
|
Option "SwapAxes" "0"
|
|
Option "InvertX" "1"
|
|
Option "InvertY" "1"
|
|
EndSection
|
|
"""
|
|
CAL_CONFIG['35r']['xorg']['270'] = """
|
|
Section "InputClass"
|
|
Identifier "calibration"
|
|
MatchProduct "stmpe-ts"
|
|
Option "Calibration" "150 3912 3843 255"
|
|
Option "SwapAxes" "1"
|
|
Option "InvertX" "0"
|
|
Option "InvertY" "0"
|
|
EndSection
|
|
"""
|
|
|
|
# Other configuration.
|
|
POINTERCAL_FILE = '/etc/pointercal'
|
|
XORGCAL_FILE = '/etc/X11/xorg.conf.d/99-calibration.conf'
|
|
ALLOWED_TYPES = CAL_CONFIG.keys()
|
|
ALLOWED_ROTATIONS = ['0', '90', '180', '270']
|
|
|
|
|
|
def read_file(filename):
|
|
"""Read specified file contents and return them, or None if file isn't
|
|
readable.
|
|
"""
|
|
try:
|
|
with open(filename, 'r') as infile:
|
|
return infile.read()
|
|
except IOError:
|
|
return None
|
|
|
|
def write_file(filename, data):
|
|
"""Write specified data to file. Returns True if data was written."""
|
|
try:
|
|
# Check if path to file exists. Create path if necessary.
|
|
directory = os.path.dirname(filename)
|
|
if not os.path.exists(directory):
|
|
os.makedirs(directory)
|
|
# Open file and write data.
|
|
with open(filename, 'w') as outfile:
|
|
outfile.write(data)
|
|
return True
|
|
except IOError, OSError:
|
|
return False
|
|
|
|
def determine_rotation():
|
|
"""Determine the rotation of the PiTFT screen by examining
|
|
/sys/class/graphics/fb1/rotate config.
|
|
"""
|
|
return read_file('/sys/class/graphics/fb1/rotate')
|
|
|
|
def determine_type():
|
|
"""Determine the type of display by examining loaded kernel modules.
|
|
"""
|
|
# Call lsmod to list kernel modules.
|
|
output = subprocess.check_output('lsmod')
|
|
# Parse out module names from lsmod response (grab first word of each line
|
|
# after the first line).
|
|
modules = map(lambda x: x.split()[0], output.splitlines()[1:])
|
|
# Check for display type based on loaded modules.
|
|
if 'stmpe_ts' in modules and 'fb_ili9340' in modules:
|
|
return '28r'
|
|
elif 'ft6x06_ts' in modules and 'fb_ili9340' in modules:
|
|
return '28c'
|
|
elif 'stmpe_ts' in modules and 'fb_hx8357d' in modules:
|
|
return '35r'
|
|
else:
|
|
return None
|
|
|
|
|
|
# Parse command line arguments.
|
|
parser = argparse.ArgumentParser(description='Automatically set the PiTFT touchscreen calibration for both /etc/pointercal and X.Org based on the current screen rotation.')
|
|
parser.add_argument('-t', '--type',
|
|
choices=ALLOWED_TYPES,
|
|
required=False,
|
|
dest='type',
|
|
help='set display type')
|
|
parser.add_argument('-r', '--rotation',
|
|
choices=ALLOWED_ROTATIONS,
|
|
required=False,
|
|
dest='rotation',
|
|
help='set calibration for specified screen rotation')
|
|
parser.add_argument('-f', '--force',
|
|
required=False,
|
|
action='store_const',
|
|
const=True,
|
|
default=False,
|
|
dest='force',
|
|
help='update calibration without prompting for confirmation')
|
|
args = parser.parse_args()
|
|
|
|
# Check that you're running as root.
|
|
if os.geteuid() != 0:
|
|
print 'Must be run as root so calibration files can be updated!'
|
|
print 'Try running with sudo, for example: sudo ./pitft_touch_cal.py'
|
|
sys.exit(1)
|
|
|
|
# Determine display type if not specified in parameters.
|
|
display_type = args.type
|
|
if display_type is None:
|
|
display_type = determine_type()
|
|
if display_type is None:
|
|
print 'Could not detect display type!'
|
|
print ''
|
|
print 'Make sure PiTFT software is configured and run again.'
|
|
print 'Alternatively, run with the --type parameter to'
|
|
print 'specify an explicit display type value.'
|
|
print ''
|
|
parser.print_help()
|
|
sys.exit(1)
|
|
|
|
# Check display type is allowed value.
|
|
if display_type not in ALLOWED_TYPES:
|
|
print 'Unsupported display type: {0}'.format(display_type)
|
|
parser.print_help()
|
|
sys.exit(1)
|
|
|
|
# Determine rotation if not specified in parameters.
|
|
rotation = args.rotation
|
|
if rotation is None:
|
|
rotation = determine_rotation()
|
|
if rotation is None:
|
|
# Error if rotation couldn't be determined.
|
|
print 'Could not detect screen rotation!'
|
|
print ''
|
|
print 'Make sure PiTFT software is configured and run again.'
|
|
print 'Alternatively, run with the --rotation parameter to'
|
|
print 'specify an explicit rotation value.'
|
|
print ''
|
|
parser.print_help()
|
|
sys.exit(1)
|
|
|
|
# Check rotation is allowed value.
|
|
rotation = rotation.strip()
|
|
if rotation not in ALLOWED_ROTATIONS:
|
|
print 'Unsupported rotation value: {0}'.format(rotation)
|
|
parser.print_help()
|
|
sys.exit(1)
|
|
|
|
print '---------------------------------'
|
|
print 'USING DISPLAY: {0}'.format(display_type)
|
|
print ''
|
|
print '---------------------------------'
|
|
print 'USING ROTATION: {0}'.format(rotation)
|
|
print ''
|
|
|
|
# Print current calibration values.
|
|
print '---------------------------------'
|
|
print 'CURRENT CONFIGURATION'
|
|
print ''
|
|
for cal_file in [POINTERCAL_FILE, XORGCAL_FILE]:
|
|
cal = read_file(cal_file)
|
|
if cal is None:
|
|
print 'Could not determine {0} configuration.'.format(cal_file)
|
|
else:
|
|
print 'Current {0} configuration:'.format(cal_file)
|
|
print cal.strip()
|
|
print ''
|
|
|
|
# Determine new calibration values.
|
|
new_pointercal = CAL_CONFIG[display_type]['pointercal'][rotation]
|
|
new_xorgcal = CAL_CONFIG[display_type]['xorg'][rotation]
|
|
|
|
# Print new calibration values.
|
|
print '---------------------------------'
|
|
print 'NEW CONFIGURATION'
|
|
print ''
|
|
for cal, filename in [(new_pointercal, POINTERCAL_FILE),
|
|
(new_xorgcal, XORGCAL_FILE)]:
|
|
print 'New {0} configuration:'.format(filename)
|
|
print cal.strip()
|
|
print ''
|
|
|
|
# Confirm calibration change with user.
|
|
if not args.force:
|
|
confirm = raw_input('Update current configuration to new configuration? [y/N]: ')
|
|
print '---------------------------------'
|
|
print ''
|
|
if confirm.lower() not in ['y', 'yes']:
|
|
print 'Exiting without updating configuration.'
|
|
sys.exit(0)
|
|
|
|
# Change calibration.
|
|
status = 0
|
|
for cal, filename in [(new_pointercal, POINTERCAL_FILE),
|
|
(new_xorgcal, XORGCAL_FILE)]:
|
|
if not write_file(filename, cal):
|
|
print 'Failed to update {0}'.format(filename)
|
|
status = 1
|
|
else:
|
|
print 'Updated {0}'.format(filename)
|
|
sys.exit(status)
|