1
0

install.sh 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. #!/bin/bash
  2. # Dune Weaver Touch - One-Command Installer
  3. # This script sets up everything needed to run Dune Weaver Touch on boot
  4. set -e
  5. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  6. ACTUAL_USER="${SUDO_USER:-$USER}"
  7. USER_HOME=$(eval echo ~$ACTUAL_USER)
  8. # Detect Raspberry Pi model
  9. PI_MODEL=$(cat /proc/device-tree/model 2>/dev/null | tr -d '\0' || echo "unknown")
  10. IS_PI5=false
  11. if [[ "$PI_MODEL" == *"Pi 5"* ]]; then
  12. IS_PI5=true
  13. fi
  14. echo "🎯 Dune Weaver Touch - Complete Installation"
  15. echo "============================================="
  16. echo "App directory: $SCRIPT_DIR"
  17. echo "User: $ACTUAL_USER"
  18. echo "Detected: $PI_MODEL"
  19. echo ""
  20. # Check if running as root
  21. if [ "$EUID" -ne 0 ]; then
  22. echo "❌ This installer must be run with sudo privileges"
  23. echo ""
  24. echo "Usage: sudo ./install.sh"
  25. echo ""
  26. exit 1
  27. fi
  28. echo "🔧 Installing system components..."
  29. echo ""
  30. # Function to install system scripts
  31. install_scripts() {
  32. echo "📄 Installing system scripts..."
  33. local scripts=("screen-on" "screen-off" "touch-monitor")
  34. for script in "${scripts[@]}"; do
  35. local source_path="$SCRIPT_DIR/scripts/$script"
  36. local target_path="/usr/local/bin/$script"
  37. if [ -f "$source_path" ]; then
  38. cp "$source_path" "$target_path"
  39. chmod 755 "$target_path"
  40. chown root:root "$target_path"
  41. echo " ✅ $script → $target_path"
  42. else
  43. echo " ⚠️ $script not found at $source_path"
  44. fi
  45. done
  46. echo " 📄 System scripts installed"
  47. }
  48. # Function to setup systemd service
  49. setup_systemd() {
  50. echo "🚀 Setting up systemd service..."
  51. local SERVICE_FILE="/etc/systemd/system/dune-weaver-touch.service"
  52. # Generate service file with linuxfb backend (works on all Pi models)
  53. # Pi 5 rotation is handled in QML (linuxfb rotation parameter requires Qt patch)
  54. echo " ℹ️ Using linuxfb backend"
  55. cat > "$SERVICE_FILE" << EOF
  56. [Unit]
  57. Description=Dune Weaver Touch Interface
  58. After=multi-user.target network-online.target
  59. Wants=network-online.target
  60. [Service]
  61. Type=simple
  62. User=$ACTUAL_USER
  63. Group=$ACTUAL_USER
  64. WorkingDirectory=$SCRIPT_DIR
  65. Environment=QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0
  66. Environment=QT_QPA_FB_HIDECURSOR=1
  67. # Hide console cursor before starting (fallback if kernel param not set)
  68. ExecStartPre=/bin/sh -c 'echo 0 > /sys/class/graphics/fbcon/cursor_blink || true'
  69. ExecStartPre=/bin/sh -c 'setterm --cursor off > /dev/tty1 || true'
  70. ExecStart=$SCRIPT_DIR/venv/bin/python $SCRIPT_DIR/main.py
  71. Restart=always
  72. RestartSec=10
  73. StartLimitInterval=200
  74. StartLimitBurst=5
  75. [Install]
  76. WantedBy=multi-user.target
  77. EOF
  78. # Enable service
  79. systemctl daemon-reload
  80. systemctl enable dune-weaver-touch.service
  81. echo " 🚀 Systemd service installed and enabled"
  82. }
  83. # Function to configure boot settings for DSI display
  84. configure_boot_settings() {
  85. echo "🖥️ Configuring boot settings for DSI display..."
  86. local CONFIG_FILE="/boot/firmware/config.txt"
  87. # Fallback to old path if new path doesn't exist
  88. [ ! -f "$CONFIG_FILE" ] && CONFIG_FILE="/boot/config.txt"
  89. if [ ! -f "$CONFIG_FILE" ]; then
  90. echo " ⚠️ config.txt not found, skipping boot configuration"
  91. return
  92. fi
  93. # Backup config.txt
  94. cp "$CONFIG_FILE" "${CONFIG_FILE}.backup.$(date +%Y%m%d_%H%M%S)"
  95. echo " ✅ Backed up config.txt"
  96. # Remove old/conflicting KMS settings
  97. sed -i '/dtoverlay=vc4-fkms-v3d/d' "$CONFIG_FILE"
  98. sed -i '/dtoverlay=vc4-xfkms-v3d/d' "$CONFIG_FILE"
  99. if [ "$IS_PI5" = true ]; then
  100. # Pi 5: KMS is enabled by default, gpu_mem is ignored (dedicated VRAM)
  101. echo " ℹ️ Pi 5 detected - KMS enabled by default, skipping vc4-kms-v3d overlay"
  102. echo " ℹ️ Pi 5 has dedicated VRAM, skipping gpu_mem setting"
  103. else
  104. # Pi 3/4: Add full KMS if not present
  105. if ! grep -q "dtoverlay=vc4-kms-v3d" "$CONFIG_FILE"; then
  106. # Find [all] section or add at end
  107. if grep -q "^\[all\]" "$CONFIG_FILE"; then
  108. sed -i '/^\[all\]/a dtoverlay=vc4-kms-v3d' "$CONFIG_FILE"
  109. else
  110. echo -e "\n[all]\ndtoverlay=vc4-kms-v3d" >> "$CONFIG_FILE"
  111. fi
  112. echo " ✅ Enabled full KMS (vc4-kms-v3d) for eglfs support"
  113. else
  114. echo " ℹ️ Full KMS already enabled"
  115. fi
  116. # Add GPU memory if not present (only for Pi 3/4 with shared memory)
  117. if ! grep -q "gpu_mem=" "$CONFIG_FILE"; then
  118. echo "gpu_mem=128" >> "$CONFIG_FILE"
  119. echo " ✅ Set GPU memory to 128MB"
  120. else
  121. echo " ℹ️ GPU memory already configured"
  122. fi
  123. fi
  124. # Disable splash screens for cleaner boot (all Pi models)
  125. if ! grep -q "disable_splash=1" "$CONFIG_FILE"; then
  126. echo "disable_splash=1" >> "$CONFIG_FILE"
  127. echo " ✅ Disabled rainbow splash"
  128. else
  129. echo " ℹ️ Rainbow splash already disabled"
  130. fi
  131. echo " 🖥️ Boot configuration updated"
  132. }
  133. # Function to setup touch rotation via udev rule
  134. setup_touch_rotation() {
  135. echo "👆 Setting up touchscreen rotation..."
  136. local UDEV_RULE_FILE="/etc/udev/rules.d/99-ft5x06-rotate.rules"
  137. # Pi 5 with linuxfb: QML handles rotation (including touch transform)
  138. # Pi 3/4: Use udev rule for touch rotation to match kernel framebuffer rotation
  139. if [ "$IS_PI5" = true ]; then
  140. echo " ℹ️ Pi 5 detected - touch rotation handled by QML, skipping udev rule"
  141. # Remove any existing touch rotation rule
  142. if [ -f "$UDEV_RULE_FILE" ]; then
  143. rm -f "$UDEV_RULE_FILE"
  144. udevadm control --reload-rules
  145. udevadm trigger
  146. echo " ✅ Removed existing touch rotation udev rule"
  147. fi
  148. else
  149. # Create udev rule for FT5x06 touch controller (180° rotation)
  150. cat > "$UDEV_RULE_FILE" << 'EOF'
  151. # Rotate FT5x06 touchscreen 180 degrees using libinput calibration matrix
  152. # Matrix format: a b c d e f 0 0 1
  153. # For 180° rotation: -1 0 1 0 -1 1 0 0 1
  154. # This inverts both X and Y axes (equivalent to 180° rotation)
  155. SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="*generic ft5x06*", \
  156. ENV{LIBINPUT_CALIBRATION_MATRIX}="-1 0 1 0 -1 1 0 0 1"
  157. EOF
  158. chmod 644 "$UDEV_RULE_FILE"
  159. echo " ✅ Touch rotation udev rule created: $UDEV_RULE_FILE"
  160. # Reload udev rules
  161. udevadm control --reload-rules
  162. udevadm trigger
  163. echo " ✅ Udev rules reloaded"
  164. echo " 👆 Touch rotation configured (180°)"
  165. fi
  166. }
  167. # Function to hide mouse cursor
  168. hide_mouse_cursor() {
  169. echo "🖱️ Configuring mouse cursor hiding..."
  170. # Install unclutter for hiding mouse cursor when idle
  171. echo " 📦 Installing unclutter..."
  172. apt install -y unclutter > /dev/null 2>&1
  173. # Create autostart directory if it doesn't exist
  174. local AUTOSTART_DIR="$USER_HOME/.config/autostart"
  175. mkdir -p "$AUTOSTART_DIR"
  176. chown -R "$ACTUAL_USER:$ACTUAL_USER" "$USER_HOME/.config"
  177. # Create unclutter autostart entry
  178. cat > "$AUTOSTART_DIR/unclutter.desktop" << 'EOF'
  179. [Desktop Entry]
  180. Type=Application
  181. Name=Unclutter
  182. Comment=Hide mouse cursor when idle
  183. Exec=unclutter -idle 0.1 -root
  184. Hidden=false
  185. NoDisplay=false
  186. X-GNOME-Autostart-enabled=true
  187. EOF
  188. chown "$ACTUAL_USER:$ACTUAL_USER" "$AUTOSTART_DIR/unclutter.desktop"
  189. echo " 🖱️ Mouse cursor hiding configured"
  190. }
  191. # Function to setup kiosk optimizations
  192. setup_kiosk_optimizations() {
  193. echo "🖥️ Setting up kiosk optimizations..."
  194. local CMDLINE_FILE="/boot/firmware/cmdline.txt"
  195. [ ! -f "$CMDLINE_FILE" ] && CMDLINE_FILE="/boot/cmdline.txt"
  196. # Determine DSI connector name based on Pi model
  197. # Pi 5 uses DSI-2 (separate DRM device), Pi 3/4 use DSI-1
  198. local DSI_CONNECTOR="DSI-1"
  199. if [ "$IS_PI5" = true ]; then
  200. DSI_CONNECTOR="DSI-2"
  201. echo " ℹ️ Pi 5 detected - using DSI-2 connector"
  202. fi
  203. # Configure cmdline.txt for display and boot
  204. if [ -f "$CMDLINE_FILE" ]; then
  205. cp "$CMDLINE_FILE" "${CMDLINE_FILE}.backup.$(date +%Y%m%d_%H%M%S)"
  206. # Add video parameter for DSI display with rotation
  207. # Check for any existing DSI video configuration
  208. if ! grep -q "video=DSI-[0-9]:800x480@60,rotate=180" "$CMDLINE_FILE"; then
  209. sed -i "s/$/ video=${DSI_CONNECTOR}:800x480@60,rotate=180/" "$CMDLINE_FILE"
  210. echo " ✅ DSI display configuration added (${DSI_CONNECTOR}:800x480@60, rotated 180°)"
  211. else
  212. echo " ℹ️ DSI display configuration already present"
  213. fi
  214. # Add quiet splash for cleaner boot
  215. if ! grep -q "quiet splash" "$CMDLINE_FILE"; then
  216. sed -i 's/$/ quiet splash/' "$CMDLINE_FILE"
  217. echo " ✅ Boot splash enabled"
  218. else
  219. echo " ℹ️ Boot splash already enabled"
  220. fi
  221. # Disable console cursor (the blinking underscore on fbcon)
  222. if ! grep -q "vt.global_cursor_default=0" "$CMDLINE_FILE"; then
  223. sed -i 's/$/ vt.global_cursor_default=0/' "$CMDLINE_FILE"
  224. echo " ✅ Console cursor disabled"
  225. else
  226. echo " ℹ️ Console cursor already disabled"
  227. fi
  228. fi
  229. echo " 🖥️ Kiosk optimizations applied"
  230. }
  231. # Function to create eglfs configuration for Qt
  232. create_eglfs_config() {
  233. echo "🖥️ Creating EGLFS configuration..."
  234. local CONFIG_FILE="$SCRIPT_DIR/eglfs_config.json"
  235. # Find the correct DRM device with DSI connector
  236. local DRM_DEVICE="/dev/dri/card0"
  237. local DSI_CONNECTOR="DSI-1"
  238. # Check each card for DSI connector
  239. for card in /sys/class/drm/card*/; do
  240. card_name=$(basename "$card")
  241. if [ -d "$card/${card_name}-DSI-1" ]; then
  242. DRM_DEVICE="/dev/dri/$card_name"
  243. DSI_CONNECTOR="DSI-1"
  244. echo " ✅ Found DSI-1 on $card_name"
  245. break
  246. elif [ -d "$card/${card_name}-DSI-2" ]; then
  247. DRM_DEVICE="/dev/dri/$card_name"
  248. DSI_CONNECTOR="DSI-2"
  249. echo " ✅ Found DSI-2 on $card_name"
  250. break
  251. fi
  252. done
  253. cat > "$CONFIG_FILE" << EOF
  254. {
  255. "device": "$DRM_DEVICE",
  256. "hwcursor": false,
  257. "pbuffers": true,
  258. "separateScreens": false,
  259. "outputs": [
  260. {
  261. "name": "$DSI_CONNECTOR",
  262. "mode": "800x480"
  263. }
  264. ]
  265. }
  266. EOF
  267. chown "$ACTUAL_USER:$ACTUAL_USER" "$CONFIG_FILE"
  268. chmod 644 "$CONFIG_FILE"
  269. echo " ✅ EGLFS config created: $CONFIG_FILE"
  270. echo " 📟 Device: $DRM_DEVICE, Connector: $DSI_CONNECTOR"
  271. }
  272. # Function to setup user groups for DRM/input access
  273. setup_user_groups() {
  274. echo "👥 Setting up user groups for hardware access..."
  275. local REQUIRED_GROUPS=("video" "render" "input" "gpio")
  276. for group in "${REQUIRED_GROUPS[@]}"; do
  277. if getent group "$group" > /dev/null 2>&1; then
  278. if id -nG "$ACTUAL_USER" | grep -qw "$group"; then
  279. echo " ℹ️ User already in '$group' group"
  280. else
  281. usermod -aG "$group" "$ACTUAL_USER"
  282. echo " ✅ Added user to '$group' group"
  283. fi
  284. else
  285. echo " ⚠️ Group '$group' does not exist, skipping"
  286. fi
  287. done
  288. echo " 👥 User groups configured"
  289. }
  290. # Function to setup console auto-login
  291. setup_console_autologin() {
  292. echo "🔑 Setting up console auto-login..."
  293. local OVERRIDE_DIR="/etc/systemd/system/getty@tty1.service.d"
  294. local OVERRIDE_FILE="$OVERRIDE_DIR/autologin.conf"
  295. # Create override directory
  296. mkdir -p "$OVERRIDE_DIR"
  297. # Create autologin override for getty@tty1
  298. cat > "$OVERRIDE_FILE" << EOF
  299. [Service]
  300. ExecStart=
  301. ExecStart=-/sbin/agetty --autologin $ACTUAL_USER --noclear %I \$TERM
  302. EOF
  303. chmod 644 "$OVERRIDE_FILE"
  304. echo " ✅ Getty autologin override created: $OVERRIDE_FILE"
  305. # Reload systemd to pick up changes
  306. systemctl daemon-reload
  307. echo " ✅ Systemd daemon reloaded"
  308. echo " 🔑 Console auto-login configured for user: $ACTUAL_USER"
  309. }
  310. # Function to setup Python virtual environment and install dependencies
  311. setup_python_environment() {
  312. echo "🐍 Setting up Python virtual environment..."
  313. # Install system Qt6 and PySide6 packages for full eglfs support
  314. echo " 📦 Installing system Qt6 and PySide6 packages..."
  315. apt update
  316. apt install -y \
  317. python3-pyside6.qtcore \
  318. python3-pyside6.qtgui \
  319. python3-pyside6.qtqml \
  320. python3-pyside6.qtquick \
  321. python3-pyside6.qtquickcontrols2 \
  322. python3-pyside6.qtquickwidgets \
  323. python3-pyside6.qtwebsockets \
  324. python3-pyside6.qtnetwork \
  325. qml6-module-qtquick \
  326. qml6-module-qtquick-controls \
  327. qml6-module-qtquick-layouts \
  328. qml6-module-qtquick-window \
  329. qml6-module-qtquick-dialogs \
  330. qml6-module-qt-labs-qmlmodels \
  331. qt6-virtualkeyboard-plugin \
  332. qml6-module-qtquick-virtualkeyboard \
  333. qt6-base-dev \
  334. qt6-declarative-dev \
  335. libqt6opengl6 \
  336. libqt6core5compat6 \
  337. libqt6network6 \
  338. libqt6websockets6 > /dev/null 2>&1
  339. echo " ✅ System Qt6/PySide6 packages installed"
  340. # Create virtual environment with system site packages
  341. if [ ! -d "$SCRIPT_DIR/venv" ]; then
  342. echo " 📦 Creating virtual environment with system site packages..."
  343. python3 -m venv --system-site-packages "$SCRIPT_DIR/venv" || {
  344. echo " ⚠️ Could not create virtual environment. Installing python3-venv..."
  345. apt install -y python3-venv python3-full
  346. python3 -m venv --system-site-packages "$SCRIPT_DIR/venv"
  347. }
  348. else
  349. echo " ℹ️ Virtual environment already exists"
  350. fi
  351. # Install non-Qt dependencies from requirements.txt
  352. echo " 📦 Installing Python dependencies..."
  353. "$SCRIPT_DIR/venv/bin/python" -m pip install --upgrade pip > /dev/null 2>&1
  354. "$SCRIPT_DIR/venv/bin/pip" install -r "$SCRIPT_DIR/requirements.txt" > /dev/null 2>&1
  355. # Change ownership to the actual user (not root)
  356. chown -R "$ACTUAL_USER:$ACTUAL_USER" "$SCRIPT_DIR/venv"
  357. echo " 🐍 Python virtual environment ready"
  358. }
  359. # Main installation process
  360. echo "Starting complete installation..."
  361. echo ""
  362. # Install everything
  363. setup_user_groups
  364. create_eglfs_config
  365. setup_python_environment
  366. install_scripts
  367. setup_systemd
  368. configure_boot_settings
  369. setup_touch_rotation
  370. hide_mouse_cursor
  371. setup_kiosk_optimizations
  372. setup_console_autologin
  373. echo ""
  374. echo "🎉 Installation Complete!"
  375. echo "========================"
  376. echo ""
  377. echo "📟 Detected: $PI_MODEL"
  378. if [ "$IS_PI5" = true ]; then
  379. echo " └─ Using Pi 5 optimized settings (DSI-2, dedicated VRAM)"
  380. fi
  381. echo ""
  382. echo "✅ Python virtual environment created at: $SCRIPT_DIR/venv"
  383. echo "✅ System scripts installed in /usr/local/bin/"
  384. echo "✅ Systemd service configured for auto-start"
  385. echo "✅ Mouse cursor hiding configured (Qt + unclutter)"
  386. echo "✅ Kiosk optimizations applied"
  387. echo "✅ Console auto-login configured"
  388. echo "✅ User groups configured (video, render, input)"
  389. echo "✅ EGLFS display config created"
  390. echo ""
  391. echo "🔧 Service Management:"
  392. echo " Start now: sudo systemctl start dune-weaver-touch"
  393. echo " Stop: sudo systemctl stop dune-weaver-touch"
  394. echo " Status: sudo systemctl status dune-weaver-touch"
  395. echo " Logs: sudo journalctl -u dune-weaver-touch -f"
  396. echo ""
  397. echo "💡 To start the service now without rebooting:"
  398. echo " sudo systemctl start dune-weaver-touch"
  399. echo ""
  400. echo "🛠️ For development/testing (run manually):"
  401. echo " cd $SCRIPT_DIR"
  402. echo " ./run.sh"
  403. echo ""
  404. echo "⚙️ To change boot/login settings later:"
  405. echo " sudo ./configure-boot.sh"
  406. echo ""
  407. # Ask if user wants to start now
  408. read -p "🤔 Would you like to start the service now? (y/N): " -n 1 -r
  409. echo
  410. if [[ $REPLY =~ ^[Yy]$ ]]; then
  411. echo "🚀 Starting Dune Weaver Touch service..."
  412. systemctl start dune-weaver-touch
  413. sleep 2
  414. systemctl status dune-weaver-touch --no-pager -l
  415. echo ""
  416. echo "✅ Service started! Check the status above."
  417. fi
  418. echo ""
  419. echo "🚀 Next Steps:"
  420. echo " 1. ⚠️ REBOOT REQUIRED for config.txt changes to take effect"
  421. echo " 2. After reboot, the app will start automatically on boot via systemd service"
  422. echo " 3. Check the logs if you encounter any issues: sudo journalctl -u dune-weaver-touch -f"
  423. echo ""
  424. echo "🎯 Installation completed successfully!"