#!/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)