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.

647 lines
26 KiB

  1. """
  2. Adafruit PiTFT Installer Script
  3. (C) Adafruit Industries, Creative Commons 3.0 - Attribution Share Alike
  4. """
  5. import time
  6. try:
  7. import click
  8. except ImportError:
  9. raise RuntimeError("The library 'Click' was not found. To install, try typing: sudo pip3 install Click==7.0")
  10. try:
  11. from adafruit_shell import Shell
  12. except ImportError:
  13. raise RuntimeError("The library 'adafruit_shell' was not found. To install, try typing: sudo pip3 install adafruit-python-shell")
  14. shell = Shell()
  15. shell.group = 'PITFT'
  16. __version__ = "3.0.0"
  17. """
  18. This is the main configuration. Displays should be placed in the order
  19. they are to appear in the menu.
  20. """
  21. config = [
  22. {
  23. "type": "28r",
  24. "menulabel": "PiTFT 2.4\", 2.8\" or 3.2\" resistive (240x320)",
  25. "product": "2.8\" resistive, PID 1601",
  26. "kernel_upgrade": False,
  27. "touchscreen": {
  28. "identifier": "STMPE Touchscreen Calibration",
  29. "product": "stmpe",
  30. "transforms": {
  31. "0": "0.988809 -0.023645 0.060523 -0.028817 1.003935 0.034176 0 0 1",
  32. "90": "0.014773 -1.132874 1.033662 1.118701 0.009656 -0.065273 0 0 1",
  33. "180": "-1.115235 -0.010589 1.057967 -0.005964 -1.107968 1.025780 0 0 1",
  34. "270": "-0.033192 1.126869 -0.014114 -1.115846 0.006580 1.050030 0 0 1",
  35. },
  36. },
  37. "overlay": "dtoverlay=pitft28-resistive,rotate={pitftrot},speed=64000000,fps=30",
  38. "calibrations": {
  39. "0": "4232 11 -879396 1 5786 -752768 65536",
  40. "90": "33 -5782 21364572 4221 35 -1006432 65536",
  41. "180": "-4273 61 16441290 4 -5772 21627524 65536",
  42. "270": "-9 5786 -784608 -4302 19 16620508 65536",
  43. },
  44. "width": 320,
  45. "height": 240,
  46. },
  47. {
  48. "type": "22",
  49. "menulabel": "PiTFT 2.2\" no touch",
  50. "product": "2.2\" no touch",
  51. "kernel_upgrade": False,
  52. "touchscreen": False,
  53. "overlay" : "dtoverlay=pitft22,rotate={pitftrot},speed=64000000,fps=30",
  54. "width": 320,
  55. "height": 240,
  56. },
  57. {
  58. "type": "28c",
  59. "menulabel": "PiTFT 2.8\" capacitive touch",
  60. "product": "2.8\" capacitive, PID 1983",
  61. "kernel_upgrade": False,
  62. "touchscreen": {
  63. "identifier": "FocalTech Touchscreen Calibration",
  64. "product": "EP0110M09",
  65. "transforms": {
  66. "0": "-1 0 1 0 -1 1 0 0 1",
  67. "90": "0 1 0 -1 0 1 0 0 1",
  68. "180": "1 0 0 0 1 0 0 0 1",
  69. "270": "0 -1 1 1 0 0 0 0 1",
  70. },
  71. },
  72. "overlay": """dtoverlay=pitft28-capacitive,speed=64000000,fps=30
  73. dtoverlay=pitft28-capacitive,{rotation}""",
  74. "calibrations": "320 65536 0 -65536 0 15728640 65536",
  75. "rotations": {
  76. "0": "rotate=90,touch-invx=true,touch-invy=true",
  77. "90": "rotate=90,touch-swapxy=true,touch-invx=true",
  78. "180": "rotate=90",
  79. "270": "rotate=270,touch-swapxy=true,touch-invy=true",
  80. },
  81. "width": 320,
  82. "height": 240,
  83. },
  84. {
  85. "type": "35r",
  86. "menulabel": "PiTFT 3.5\" resistive touch",
  87. "product": "3.5\" Resistive",
  88. "kernel_upgrade": False,
  89. "touchscreen": {
  90. "identifier": "STMPE Touchscreen Calibration",
  91. "product": "stmpe",
  92. "transforms": {
  93. "0": "-1.098388 0.003455 1.052099 0.005512 -1.093095 1.026309 0 0 1",
  94. "90": "-0.000087 1.094214 -0.028826 -1.091711 -0.004364 1.057821 0 0 1",
  95. "180": "1.102807 0.000030 -0.066352 0.001374 1.085417 -0.027208 0 0 1",
  96. "270": "0.003893 -1.087542 1.025913 1.084281 0.008762 -0.060700 0 0 1",
  97. },
  98. },
  99. "overlay": "dtoverlay=pitft35-resistive,rotate={pitftrot},speed=20000000,fps=20",
  100. "calibrations": {
  101. "0": "5724 -6 -1330074 26 8427 -1034528 65536",
  102. "90": "5 8425 -978304 -5747 61 22119468 65536",
  103. "180": "-5682 -1 22069150 13 -8452 32437698 65536",
  104. "270": "3 -8466 32440206 5703 -1 -1308696 65536",
  105. },
  106. "width": 480,
  107. "height": 320,
  108. "x11_scale": 1.5,
  109. },
  110. {
  111. "type": "st7789_240x240",
  112. "menulabel": "PiTFT Mini 1.3\" or 1.54\" display",
  113. "product": "1.54\" or 1.3\" no touch",
  114. "kernel_upgrade": True,
  115. "overlay_src": "overlays/minipitft13-overlay.dts",
  116. "overlay_dest": "/boot/overlays/drm-minipitft13.dtbo",
  117. "overlay": "dtoverlay=drm-minipitft13,rotate={pitftrot}",
  118. "width": 240,
  119. "height": 240,
  120. },
  121. {
  122. "type": "st7789_240x320",
  123. "menulabel": "ST7789V 2.0\" no touch",
  124. "product": "2.0\" no touch",
  125. "kernel_upgrade": True,
  126. "overlay_src": "overlays/st7789v_240x320-overlay.dts",
  127. "overlay_dest": "/boot/overlays/drm-st7789v_240x320.dtbo",
  128. "overlay": "dtoverlay=drm-st7789v_240x320,rotate={pitftrot}",
  129. "width": 320,
  130. "height": 240,
  131. "rotate": False,
  132. },
  133. {
  134. "type": "st7789_240x135",
  135. "menulabel": "MiniPiTFT 1.14\" display",
  136. "product": "1.14\" no touch",
  137. "kernel_upgrade": True,
  138. "overlay_src": "overlays/minipitft114-overlay.dts",
  139. "overlay_dest": "/boot/overlays/drm-minipitft114.dtbo",
  140. "overlay": "dtoverlay=drm-minipitft114,rotation={pitftrot}",
  141. "width": 240,
  142. "height": 135,
  143. },
  144. ]
  145. PITFT_ROTATIONS = ("90", "180", "270", "0")
  146. UPDATE_DB = False
  147. SYSTEMD = None
  148. pitft_config = None
  149. pitftrot = None
  150. auto_reboot = None
  151. def warn_exit(message):
  152. shell.warn(message)
  153. shell.exit(1)
  154. def uninstall_cb(ctx, param, value):
  155. if not value or ctx.resilient_parsing:
  156. return
  157. uninstall()
  158. def print_version(ctx, param, value):
  159. if not value or ctx.resilient_parsing:
  160. return
  161. print("Adafruit PiTFT Helper v{}".format(__version__))
  162. shell.exit(1)
  163. def progress(ellipsis_count):
  164. for i in range(ellipsis_count):
  165. print("...", end='')
  166. time.sleep(1)
  167. print("")
  168. def sysupdate():
  169. global UPDATE_DB
  170. if not UPDATE_DB:
  171. print("Updating apt indexes...", end='')
  172. progress(3)
  173. if not shell.run_command('sudo apt update', True):
  174. warn_exit("Apt failed to update indexes!")
  175. if not shell.run_command('sudo apt-get update', True):
  176. warn_exit("Apt failed to update indexes!")
  177. print("Reading package lists...")
  178. progress(3)
  179. UPDATE_DB = True
  180. return True
  181. ############################ Sub-Scripts ############################
  182. def softwareinstall():
  183. print("Installing Pre-requisite Software...This may take a few minutes!")
  184. if not shell.run_command("apt-get install -y libts0", True):
  185. if not shell.run_command("apt-get install -y tslib"):
  186. warn_exit("Apt failed to install TSLIB!")
  187. if not shell.run_command("apt-get install -y bc fbi git python-dev python-pip python-smbus python-spidev evtest libts-bin device-tree-compiler"):
  188. warn_exit("Apt failed to install software!")
  189. if not shell.run_command("pip3 install evdev"):
  190. warn_exit("Pip failed to install software!")
  191. return True
  192. def uninstall_bootconfigtxt():
  193. """Remove any old flexfb/fbtft stuff"""
  194. if shell.pattern_search("/boot/config.txt", "adafruit-pitft-helper"):
  195. print("Already have an adafruit-pitft-helper section in /boot/config.txt.")
  196. print("Removing old section...")
  197. shell.run_command("cp /boot/config.txt /boot/configtxt.bak")
  198. shell.pattern_replace("/boot/config.txt", '\n# --- added by adafruit-pitft-helper.*?\n# --- end adafruit-pitft-helper.*?\n', multi_line=True)
  199. return True
  200. def uninstall_etc_modules():
  201. """Remove any old flexfb/fbtft stuff"""
  202. shell.run_command('rm -f /etc/modprobe.d/fbtft.conf')
  203. shell.pattern_replace("/etc/modules", 'spi-bcm2835')
  204. shell.pattern_replace("/etc/modules", 'flexfb')
  205. shell.pattern_replace("/etc/modules", 'fbtft_device')
  206. return True
  207. def update_configtxt(rotation_override=None):
  208. """update /boot/config.txt with appropriate values"""
  209. uninstall_bootconfigtxt()
  210. uninstall_etc_modules()
  211. overlay = pitft_config['overlay']
  212. if "{pitftrot}" in overlay:
  213. rotation = str(rotation_override) if rotation_override is not None else pitftrot
  214. overlay = overlay.format(pitftrot=rotation)
  215. if "{rotation}" in overlay and isinstance(pitft_config['rotations'], dict):
  216. overlay = overlay.format(rotation=pitft_config['rotations'][pitftrot])
  217. if "overlay_src" in pitft_config and "overlay_dest" in pitft_config:
  218. shell.run_command("dtc -@ -I dts -O dtb -o {dest} {src}".format(dest=pitft_config['overlay_dest'], src=pitft_config['overlay_src']))
  219. print("############# UPGRADING KERNEL ###############")
  220. print("Updating packages...")
  221. if not shell.run_command("sudo apt-get update", True):
  222. warn_exit("Apt failed to update itself!")
  223. print("Upgrading packages...")
  224. if not shell.run_command("sudo apt-get upgrade", True):
  225. warn_exit("Apt failed to install software!")
  226. print("Installing Kernel Headers...")
  227. if not shell.run_command("apt-get install -y raspberrypi-kernel-headers", True):
  228. warn_exit("Apt failed to install software!")
  229. if not shell.isdir("/lib/modules/{}/build".format(shell.release())):
  230. warn_exit("Kernel was updated, please reboot now and re-run script!")
  231. shell.pushd("st7789_module")
  232. if not shell.run_command("make -C /lib/modules/$(uname -r)/build M=$(pwd) modules"):
  233. warn_exit("Apt failed to compile ST7789V drivers!")
  234. shell.run_command("mv /lib/modules/{rel}/kernel/drivers/gpu/drm/tiny/mi0283qt.ko /lib/modules/{rel}/kernel/drivers/gpu/drm/tiny/mi0283qt.BACK".format(rel=shell.release()))
  235. shell.run_command("mv /lib/modules/{rel}/kernel/drivers/staging/fbtft/fb_st7789v.ko /lib/modules/{rel}/kernel/drivers/gpu/drm/tiny/mi0283qt.BACK".format(rel=shell.release()))
  236. shell.run_command("mv st7789v_ada.ko /lib/modules/{rel}/kernel/drivers/gpu/drm/tiny/mi0283qt.ko".format(rel=shell.release()))
  237. shell.run_command("mv fb_st7789v.ko /lib/modules/{rel}/kernel/drivers/staging/fbtft/fb_st7789v.ko".format(rel=shell.release()))
  238. shell.popd()
  239. shell.write_text_file("/boot/config.txt", """
  240. # --- added by adafruit-pitft-helper {date} ---
  241. hdmi_force_hotplug=1 # required for cases when HDMI is not plugged in!
  242. dtparam=spi=on
  243. dtparam=i2c1=on
  244. dtparam=i2c_arm=on
  245. {overlay}
  246. # --- end adafruit-pitft-helper {date} ---
  247. """.format(date=shell.date(), overlay=overlay))
  248. return True
  249. def update_udev():
  250. shell.write_text_file("/etc/udev/rules.d/95-touchmouse.rules", """
  251. SUBSYSTEM=="input", ATTRS{name}=="touchmouse", ENV{DEVNAME}=="*event*", SYMLINK+="input/touchscreen"
  252. """, append=False)
  253. shell.write_text_file("/etc/udev/rules.d/95-ftcaptouch.rules", """
  254. SUBSYSTEM=="input", ATTRS{name}=="EP0110M09", ENV{DEVNAME}=="*event*", SYMLINK+="input/touchscreen"
  255. SUBSYSTEM=="input", ATTRS{name}=="generic ft5x06*", ENV{DEVNAME}=="*event*", SYMLINK+="input/touchscreen"
  256. """, append=False)
  257. shell.write_text_file("/etc/udev/rules.d/95-stmpe.rules", """
  258. SUBSYSTEM=="input", ATTRS{name}=="*stmpe*", ENV{DEVNAME}=="*event*", SYMLINK+="input/touchscreen"
  259. """, append=False)
  260. return True
  261. def update_pointercal():
  262. if "calibrations" in pitft_config:
  263. if isinstance(pitft_config["calibrations"], dict):
  264. shell.write_text_file("/etc/pointercal", pitft_config["calibrations"][pitftrot])
  265. else:
  266. shell.write_text_file("/etc/pointercal", pitft_config["calibrations"])
  267. return True
  268. def install_console():
  269. print("Set up main console turn on")
  270. if not shell.pattern_search("/boot/cmdline.txt", 'fbcon=map:10 fbcon=font:VGA8x8'):
  271. print("Updating /boot/cmdline.txt")
  272. shell.pattern_replace("/boot/cmdline.txt", "rootwait", "rootwait fbcon=map:10 fbcon=font:VGA8x8")
  273. else:
  274. print("/boot/cmdline.txt already updated")
  275. print("Turning off console blanking")
  276. # pre-stretch this is what you'd do:
  277. if shell.exists("/etc/kbd/config"):
  278. shell.pattern_replace("/etc/kbd/config", "BLANK_TIME=.*", "BLANK_TIME=0")
  279. # as of stretch....
  280. # removing any old version
  281. shell.pattern_replace("/etc/rc.local", '# disable console blanking.*')
  282. shell.pattern_replace("/etc/rc.local", 'sudo sh -c "TERM=linux setterm -blank.*')
  283. shell.pattern_replace("/etc/rc.local", '^exit 0', "# disable console blanking on PiTFT\\nsudo sh -c \"TERM=linux setterm -blank 0 >/dev/tty0\"\\nexit 0")
  284. shell.reconfig("/etc/default/console-setup", "^.*FONTFACE.*$", "FONTFACE=\"Terminus\"")
  285. shell.reconfig("/etc/default/console-setup", "^.*FONTSIZE.*$", "FONTSIZE=\"6x12\"")
  286. print("Setting raspi-config to boot to console w/o login...")
  287. shell.chdir(target_homedir)
  288. shell.run_command("raspi-config nonint do_boot_behaviour B2")
  289. # remove fbcp
  290. shell.pattern_replace("/etc/rc.local", "^.*fbcp.*$")
  291. return True
  292. def uninstall_console():
  293. print("Removing console fbcon map from /boot/cmdline.txt")
  294. shell.pattern_replace("/boot/cmdline.txt", 'rootwait fbcon=map:10 fbcon=font:VGA8x8', 'rootwait')
  295. print("Screen blanking time reset to 10 minutes")
  296. if shell.exists("/etc/kbd/config"):
  297. shell.pattern_replace("/boot/cmdline.txt", 'BLANK_TIME=0', 'BLANK_TIME=10')
  298. shell.pattern_replace("/etc/rc.local", '^# disable console blanking.*')
  299. shell.pattern_replace("/etc/rc.local", '^sudo sh -c "TERM=linux.*')
  300. return True
  301. def install_fbcp():
  302. print("Installing cmake...")
  303. if not shell.run_command("apt-get --yes --allow-downgrades --allow-remove-essential --allow-change-held-packages install cmake", True):
  304. warn_exit("Apt failed to install software!")
  305. print("Downloading rpi-fbcp...")
  306. shell.pushd("/tmp")
  307. shell.run_command("curl -sLO https://github.com/adafruit/rpi-fbcp/archive/master.zip")
  308. print("Uncompressing rpi-fbcp...")
  309. shell.run_command("rm -rf /tmp/rpi-fbcp-master")
  310. if not shell.run_command("unzip master.zip", True):
  311. warn_exit("Failed to uncompress fbcp!")
  312. shell.chdir("rpi-fbcp-master")
  313. shell.run_command("mkdir build")
  314. shell.chdir("build")
  315. print("Building rpi-fbcp...")
  316. shell.write_text_file("../CMakeLists.txt", "\nset (CMAKE_C_FLAGS \"-std=gnu99 ${CMAKE_C_FLAGS}\")")
  317. if not shell.run_command("cmake ..", True):
  318. warn_exit("Failed to cmake fbcp!")
  319. if not shell.run_command("make", True):
  320. warn_exit("Failed to make fbcp!")
  321. print("Installing rpi-fbcp...")
  322. shell.run_command("install fbcp /usr/local/bin/fbcp")
  323. shell.popd()
  324. shell.run_command("rm -rf /tmp/rpi-fbcp-master")
  325. # Start fbcp in the appropriate place, depending on init system:
  326. if SYSTEMD:
  327. # Add fbcp to /etc/rc.local:
  328. print("We have sysvinit, so add fbcp to /etc/rc.local...")
  329. if shell.pattern_search("/etc/rc.local", "fbcp"):
  330. # fbcp already in rc.local, but make sure correct:
  331. shell.pattern_replace("/etc/rc.local", "^.*fbcp.*$", "/usr/local/bin/fbcp \&")
  332. else:
  333. # Insert fbcp into rc.local before final 'exit 0':
  334. shell.pattern_replace("/etc/rc.local", "^exit 0", "/usr/local/bin/fbcp \&\\nexit 0")
  335. else:
  336. # Install fbcp systemd unit, first making sure it's not in rc.local:
  337. uninstall_fbcp_rclocal()
  338. print("We have systemd, so install fbcp systemd unit...")
  339. if not install_fbcp_unit():
  340. shell.bail("Unable to install fbcp unit file")
  341. shell.run_command("sudo systemctl enable fbcp.service")
  342. # if there's X11 installed...
  343. if shell.exists("/etc/lightdm"):
  344. print("Setting raspi-config to boot to desktop w/o login...")
  345. shell.run_command("raspi-config nonint do_boot_behaviour B4")
  346. # Disable overscan compensation (use full screen):
  347. shell.run_command("raspi-config nonint do_overscan 1")
  348. # Set up HDMI parameters:
  349. print("Configuring boot/config.txt for forced HDMI")
  350. shell.reconfig("/boot/config.txt", "^.*hdmi_force_hotplug.*$", "hdmi_force_hotplug=1")
  351. shell.reconfig("/boot/config.txt", "^.*hdmi_group.*$", "hdmi_group=2")
  352. shell.reconfig("/boot/config.txt", "^.*hdmi_mode.*$", "hdmi_mode=87")
  353. shell.reconfig("/boot/config.txt", "^[^#]*dtoverlay=vc4-fkms-v3d.*$", "#dtoverlay=vc4-fkms-v3d")
  354. # if there's X11 installed...
  355. scale = 1
  356. if shell.exists("/etc/lightdm"):
  357. if "x11_scale" in pitft_config:
  358. scale = pitft_config["x11_scale"]
  359. else:
  360. scale = 2
  361. WIDTH = int(pitft_config['width'] * scale)
  362. HEIGHT = int(pitft_config['height'] * scale)
  363. shell.reconfig("/boot/config.txt", "^.*hdmi_cvt.*$", "hdmi_cvt={} {} 60 1 0 0 0".format(WIDTH, HEIGHT))
  364. if pitftrot == "90" or pitftrot == "270" or ("rotate" in pitft_config and not pitft_config['rotate']):
  365. # dont rotate HDMI on 90 or 270
  366. shell.reconfig("/boot/config.txt", "^.*display_hdmi_rotate.*$", "")
  367. if pitftrot in ("0", "180"):
  368. display_rotate = "3" if pitftrot == "180" else "0"
  369. shell.reconfig("/boot/config.txt", "^.*display_hdmi_rotate.*$", "display_hdmi_rotate={}".format(display_rotate))
  370. # Because we rotate HDMI we have to 'unrotate' the TFT by overriding pitftrot!
  371. if not update_configtxt(90):
  372. shell.bail("Unable to update /boot/config.txt")
  373. return True
  374. def install_fbcp_unit():
  375. shell.write_text_file("/etc/systemd/system/fbcp.service",
  376. """[Unit]
  377. Description=Framebuffer copy utility for PiTFT
  378. After=network.target
  379. [Service]
  380. Type=simple
  381. ExecStartPre=/bin/sleep 10
  382. ExecStart=/usr/local/bin/fbcp
  383. [Install]
  384. WantedBy=multi-user.target
  385. """, append=False)
  386. return True
  387. def uninstall_fbcp():
  388. uninstall_fbcp_rclocal()
  389. # Enable overscan compensation
  390. shell.run_command("sudo systemctl disable fbcp.service")
  391. # Set up HDMI parameters:
  392. shell.run_command("raspi-config nonint do_overscan 0")
  393. print("Configuring boot/config.txt for default HDMI")
  394. shell.reconfig("/boot/config.txt", "^.*hdmi_force_hotplug.*$", "hdmi_force_hotplug=0")
  395. shell.reconfig("/boot/config.txt", "^.*#.*dtoverlay=vc4-fkms-v3d.*$", "dtoverlay=vc4-fkms-v3d")
  396. shell.pattern_replace("/boot/config.txt", '^hdmi_group=2.*$')
  397. shell.pattern_replace("/boot/config.txt", '^hdmi_mode=87.*$')
  398. shell.pattern_replace("/boot/config.txt", '^hdmi_cvt=.*$')
  399. return True
  400. def uninstall_fbcp_rclocal():
  401. """Remove fbcp from /etc/rc.local:"""
  402. print("Remove fbcp from /etc/rc.local, if it's there...")
  403. shell.pattern_replace("/etc/rc.local", '^.*fbcp.*$')
  404. return True
  405. def update_xorg():
  406. if "touchscreen" in pitft_config:
  407. transform = "Option \"TransformationMatrix\" \"{}\"".format(pitft_config["touchscreen"]["transforms"][pitftrot])
  408. shell.write_text_file("/usr/share/X11/xorg.conf.d/20-calibration.conf", """
  409. Section "InputClass"
  410. Identifier "{identifier}"
  411. MatchProduct "{product}"
  412. MatchDevicePath "/dev/input/event*"
  413. Driver "libinput"
  414. {transform}
  415. EndSection
  416. """.format(
  417. identifier=pitft_config["touchscreen"]["identifier"],
  418. product=pitft_config["touchscreen"]["product"],
  419. transform=transform
  420. ),
  421. append=False,
  422. )
  423. return True
  424. def get_config_types():
  425. types = []
  426. for item in config:
  427. types.append(item["type"])
  428. return types
  429. def get_config(type):
  430. for item in config:
  431. if item["type"] == type:
  432. return item
  433. return None
  434. def uninstall():
  435. shell.info("Uninstalling PiTFT")
  436. uninstall_bootconfigtxt()
  437. uninstall_console()
  438. uninstall_fbcp()
  439. uninstall_fbcp_rclocal()
  440. uninstall_etc_modules()
  441. success()
  442. def success():
  443. global auto_reboot
  444. shell.info("Success!")
  445. print("""
  446. Settings take effect on next boot.
  447. """)
  448. if auto_reboot is None:
  449. auto_reboot = shell.prompt("REBOOT NOW?", default="y")
  450. if not auto_reboot:
  451. print("Exiting without reboot.")
  452. shell.exit()
  453. print("Reboot started...")
  454. shell.reboot()
  455. shell.exit()
  456. ####################################################### MAIN
  457. target_homedir = "/home/pi"
  458. @click.command()
  459. @click.option('-v', '--version', is_flag=True, callback=print_version, expose_value=False, is_eager=True, help="Print version information")
  460. @click.option('-u', '--user', nargs=1, default="/home/pi", type=str, help="Specify path of primary user's home directory", show_default=True)
  461. @click.option('--display', nargs=1, default=None, help="Specify a display option (1-{}) or type {}".format(len(config), get_config_types()))
  462. @click.option('--rotation', nargs=1, default=None, type=int, help="Specify a rotation option (1-4) or degrees {}".format(tuple(sorted([int(x) for x in PITFT_ROTATIONS]))))
  463. @click.option('--install-type', nargs=1, default=None, type=click.Choice(['fbcp', 'console', 'uninstall']), help="Installation Type")
  464. @click.option('--reboot', nargs=1, default=None, type=click.Choice(['yes', 'no']), help="Specify whether to reboot after the script is finished")
  465. def main(user, display, rotation, install_type, reboot):
  466. global target_homedir, pitft_config, pitftrot, auto_reboot
  467. shell.clear()
  468. if user != target_homedir:
  469. target_homedir = user
  470. print("Homedir = {}".format(target_homedir))
  471. print("""This script downloads and installs
  472. PiTFT Support using userspace touch
  473. controls and a DTO for display drawing.
  474. one of several configuration files.
  475. Run time of up to 5 minutes. Reboot required!
  476. """)
  477. if reboot is not None:
  478. auto_reboot = reboot.lower() == 'yes'
  479. if install_type == "uninstall":
  480. uninstall()
  481. if display in [str(x) for x in range(1, len(config) + 1)]:
  482. pitft_config = config[int(display) - 1]
  483. print("Display Type: {}".format(pitft_config["menulabel"]))
  484. if pitft_config['kernel_upgrade']:
  485. print("WARNING! WILL UPGRADE YOUR KERNEL TO LATEST")
  486. elif display in get_config_types():
  487. pitft_config = get_config(display)
  488. print("Display Type: {}".format(pitft_config["menulabel"]))
  489. if pitft_config['kernel_upgrade']:
  490. print("WARNING! WILL UPGRADE YOUR KERNEL TO LATEST")
  491. else:
  492. # Build menu from config
  493. selections = []
  494. for item in config:
  495. option = "{} ({}x{})".format(item['menulabel'], item['width'], item['height'])
  496. if item['kernel_upgrade']:
  497. option += " - WARNING! WILL UPGRADE YOUR KERNEL TO LATEST"
  498. selections.append(option)
  499. selections.append("Uninstall PiTFT")
  500. selections.append("Quit without installing")
  501. PITFT_SELECT = shell.select_n("Select configuration:", selections)
  502. if PITFT_SELECT == len(config) + 2:
  503. shell.exit(1)
  504. if PITFT_SELECT == len(config) + 1:
  505. uninstall()
  506. pitft_config = config[PITFT_SELECT - 1]
  507. if 1 <= rotation <= 4:
  508. pitftrot = PITFT_ROTATIONS[rotation - 1]
  509. print("Rotation: {}".format(pitftrot))
  510. elif str(rotation) in PITFT_ROTATIONS:
  511. pitftrot = str(rotation)
  512. print("Rotation: {}".format(pitftrot))
  513. else:
  514. PITFT_ROTATE = shell.select_n(
  515. "Select rotation:", (
  516. "90 degrees (landscape)",
  517. "180 degrees (portrait)",
  518. "270 degrees (landscape)",
  519. "0 degrees (portrait)"
  520. ))
  521. pitftrot = PITFT_ROTATIONS[PITFT_ROTATE - 1]
  522. # check init system (technique borrowed from raspi-config):
  523. shell.info('Checking init system...')
  524. if shell.run_command("which systemctl", True) and shell.run_command("systemctl | grep '\-\.mount'", True):
  525. SYSTEMD = True
  526. print("Found systemd")
  527. elif os.path.isfile("/etc/init.d/cron") and not os.path.islink("/etc/init.d/cron"):
  528. SYSTEMD = False
  529. print("Found sysvinit")
  530. else:
  531. shell.bail("Unrecognised init system")
  532. if shell.grep("boot", "/proc/mounts"):
  533. print("/boot is mounted")
  534. else:
  535. print("/boot must be mounted. if you think it's not, quit here and try: sudo mount /dev/mmcblk0p1 /boot")
  536. if shell.prompt("Continue?"):
  537. print("Proceeding.")
  538. else:
  539. shell.bail("Aborting.")
  540. if not shell.isdir(target_homedir):
  541. shell.bail("{} must be an existing directory (use -u /home/foo to specify)".format(target_homedir))
  542. shell.info("System update")
  543. if not sysupdate():
  544. shell.bail("Unable to apt-get update")
  545. shell.info("Installing Python libraries & Software...")
  546. if not softwareinstall():
  547. shell.bail("Unable to install software")
  548. shell.info("Updating /boot/config.txt...")
  549. if not update_configtxt():
  550. shell.bail("Unable to update /boot/config.txt")
  551. if "touchscreen" in pitft_config:
  552. shell.info("Updating SysFS rules for Touchscreen...")
  553. if not update_udev():
  554. shell.bail("Unable to update /etc/udev/rules.d")
  555. shell.info("Updating TSLib default calibration...")
  556. if not update_pointercal():
  557. shell.bail("Unable to update /etc/pointercal")
  558. # ask for console access
  559. if install_type == "console" or (install_type is None and shell.prompt("Would you like the console to appear on the PiTFT display?")):
  560. shell.info("Updating console to PiTFT...")
  561. if not uninstall_fbcp():
  562. shell.bail("Unable to uninstall fbcp")
  563. if not install_console():
  564. shell.bail("Unable to configure console")
  565. else:
  566. shell.info("Making sure console doesn't use PiTFT")
  567. if not uninstall_console():
  568. shell.bail("Unable to configure console")
  569. if install_type == "fbcp" or (install_type is None and shell.prompt("Would you like the HDMI display to mirror to the PiTFT display?")):
  570. shell.info("Adding FBCP support...")
  571. if not install_fbcp():
  572. shell.bail("Unable to configure fbcp")
  573. if shell.exists("/etc/lightdm"):
  574. shell.info("Updating X11 default calibration...")
  575. if not update_xorg():
  576. shell.bail("Unable to update calibration")
  577. else:
  578. if not uninstall_fbcp():
  579. shell.bail("Unable to uninstall fbcp")
  580. success()
  581. # Main function
  582. if __name__ == "__main__":
  583. shell.require_root()
  584. main()