install.sh 15 KB

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