Add tools to diagnose and fix second-launch failure issue
New issue: Paintstorm works on first launch after starting OTD, but fails on second launch. Pressure stops working on subsequent launches. Root cause: Native devices (id=11, 24) are being re-enabled between launches, causing Paintstorm to switch back to the dead native device instead of the working OTD virtual device. New diagnostic tools: - test-paintstorm-relaunch-issue.sh: Automated test that reproduces the issue * Guides user through first and second launch * Captures device state after each launch * Compares states to identify what changed * Provides specific fix based on findings - track-device-state-changes.sh: Manual device state tracker * Take snapshots of xinput state at any time * Compare snapshots to see what changed * Useful for detailed debugging * Stores snapshots in /tmp/device-snapshots New fix: - paintstorm-with-otd-wrapper.sh: Smart launcher wrapper * ALWAYS disables native devices before launch * Ensures OTD is running * Verifies OTD virtual device exists * Prevents device state from affecting subsequent launches * This is the recommended way to use Paintstorm with OTD Usage for second-launch fix: # Instead of ~/paintstorm, use: ./paintstorm-with-otd-wrapper.sh # Or install it: cp paintstorm-with-otd-wrapper.sh ~/paintstorm-otd chmod +x ~/paintstorm-otd ~/paintstorm-otd
+500 · 3 files changed
-
paintstorm-with-otd-wrapper.sh+42 −0 -
test-paintstorm-relaunch-issue.sh+222 −0 -
track-device-state-changes.sh+236 −0
@@ -0,0 +1,42 @@ +#!/bin/bash +# Paintstorm launcher wrapper that ensures OTD configuration stays correct +# This fixes the "second launch doesn't work" issue + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +# Ensure OTD is running +if ! pgrep -f otd-daemon > /dev/null; then + echo -e "${YELLOW}Starting OpenTabletDriver...${NC}" + otd-daemon & + sleep 2 +fi + +# ALWAYS disable native devices before launch +# This prevents them from interfering even if they got re-enabled +echo -e "${BLUE}Configuring tablet devices...${NC}" +xinput disable 11 2>/dev/null || true # Native stylus +xinput disable 24 2>/dev/null || true # Native eraser + +# Verify OTD virtual device exists +if ! xinput list | grep -q "OpenTabletDriver"; then + echo -e "${RED}ERROR: OTD virtual device not found!${NC}" + echo "OTD might not have detected your tablet." + echo "Try: otd detect" + exit 1 +fi + +# Show status +echo -e "${GREEN}✓ Native devices disabled${NC}" +echo -e "${GREEN}✓ OTD virtual device active${NC}" +echo "" + +# Launch Paintstorm +~/paintstorm "$@" + +# Note: We don't re-enable native devices after exit +# If you want to use native HID, stop OTD first: +# pkill otd-daemon && xinput enable 11 && xinput enable 24 @@ -0,0 +1,222 @@ +#!/bin/bash +# Automated test to reproduce the second-launch issue + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +BOLD='\033[1m' +NC='\033[0m' + +echo "==========================================" +echo "Paintstorm Relaunch Issue Test" +echo "==========================================" +echo "" + +check_device_state() { + local when="$1" + + echo -e "${BOLD}=== Device State: $when ===${NC}" + + # Check OTD + if pgrep -f otd-daemon > /dev/null; then + echo -e "OTD daemon: ${GREEN}RUNNING${NC}" + else + echo -e "OTD daemon: ${RED}STOPPED${NC}" + fi + + # Check native devices + echo "" + echo "Native devices:" + for id in 11 24; do + if xinput list | grep -q "id=$id"; then + state=$(xinput list | grep "id=$id") + if echo "$state" | grep -q "floating"; then + echo -e " id=$id: ${GREEN}DISABLED (floating)${NC}" + else + echo -e " id=$id: ${YELLOW}ENABLED (attached)${NC}" + echo " $state" + fi + else + echo " id=$id: NOT FOUND" + fi + done + + # Check virtual devices + echo "" + echo "OTD virtual devices:" + if xinput list | grep -q "OpenTabletDriver"; then + xinput list | grep "OpenTabletDriver" | while read line; do + echo -e " ${GREEN}$line${NC}" + done + else + echo -e " ${RED}NOT FOUND${NC}" + fi + + # Quick activity test + echo "" + echo -n "Quick activity test (move pen now, 2 seconds)... " + + native_events=0 + virtual_events=0 + + if xinput list | grep -q "id=11"; then + native_events=$(timeout 2 xinput test 11 2>&1 | grep -c "motion" || echo "0") + fi + + virtual_id=$(xinput list | grep "OpenTabletDriver.*stylus" | head -1 | grep -o 'id=[0-9]*' | cut -d= -f2) + if [ -n "$virtual_id" ]; then + virtual_events=$(timeout 2 xinput test "$virtual_id" 2>&1 | grep -c "motion" || echo "0") + fi + + echo "Done" + echo " Native (id=11) events: $native_events" + echo " Virtual (id=$virtual_id) events: $virtual_events" + + if [ "$virtual_events" -gt 0 ]; then + echo -e " ${GREEN}✓ Using OTD virtual device${NC}" + elif [ "$native_events" -gt 0 ]; then + echo -e " ${YELLOW}⚠ Using native device (might be a problem)${NC}" + else + echo -e " ${RED}✗ No activity detected${NC}" + fi + + echo "" + echo "==========================================" + echo "" +} + +echo "This script will help diagnose why Paintstorm works the first" +echo "time you launch it but not the second time." +echo "" +echo "The test will:" +echo "1. Check device state now" +echo "2. Wait for you to launch Paintstorm (first time)" +echo "3. Check device state after you close it" +echo "4. Wait for you to launch Paintstorm (second time)" +echo "5. Compare states to find what changed" +echo "" +read -p "Press Enter to start the test..." + +echo "" +echo "==========================================" +echo "Step 1: Initial State" +echo "==========================================" +echo "" + +check_device_state "Before first launch" + +echo "" +echo "==========================================" +echo "Step 2: First Launch" +echo "==========================================" +echo "" +echo "Now launch Paintstorm and test that pressure works." +echo "Then CLOSE Paintstorm and come back here." +echo "" +read -p "Press Enter after you've closed Paintstorm from first launch..." + +echo "" +check_device_state "After first launch (closed)" + +# Save state for comparison +native_state_after_first=$(xinput list | grep "id=11" | grep -o '\[.*\]') +virtual_state_after_first=$(xinput list | grep "OpenTabletDriver.*stylus" | head -1 | grep -o '\[.*\]') + +echo "" +echo "==========================================" +echo "Step 3: Second Launch" +echo "==========================================" +echo "" +echo "Now launch Paintstorm AGAIN (second time)." +echo "Test if pressure works." +echo "Then CLOSE Paintstorm and come back here." +echo "" +read -p "Press Enter after you've closed Paintstorm from second launch..." + +echo "" +check_device_state "After second launch (closed)" + +# Save state for comparison +native_state_after_second=$(xinput list | grep "id=11" | grep -o '\[.*\]') +virtual_state_after_second=$(xinput list | grep "OpenTabletDriver.*stylus" | head -1 | grep -o '\[.*\]') + +echo "" +echo "==========================================" +echo "ANALYSIS" +echo "==========================================" +echo "" + +echo "Device state comparison:" +echo "" +printf "%-30s %-25s %-25s\n" "Device" "After 1st Launch" "After 2nd Launch" +printf "%-30s %-25s %-25s\n" "------------------------------" "-------------------------" "-------------------------" +printf "%-30s %-25s %-25s\n" "Native device (id=11)" "$native_state_after_first" "$native_state_after_second" +printf "%-30s %-25s %-25s\n" "Virtual device" "$virtual_state_after_first" "$virtual_state_after_second" + +echo "" + +if [ "$native_state_after_first" != "$native_state_after_second" ]; then + echo -e "${YELLOW}⚠ PROBLEM DETECTED:${NC} Native device state changed!" + echo "" + echo "The native device state is different between the two launches." + + if echo "$native_state_after_second" | grep -q "slave.*pointer"; then + echo "" + echo -e "${RED}✗ Native device got RE-ENABLED after first launch!${NC}" + echo "" + echo "This is the bug. Something is re-enabling the native device," + echo "which causes Paintstorm to use it on the second launch." + echo "But the native device is DEAD (OTD has exclusive hidraw access)," + echo "so pressure doesn't work." + echo "" + echo "SOLUTION: Ensure native devices stay disabled" + echo "" + echo "Option A: Disable them before each launch:" + echo " xinput disable 11 && xinput disable 24 && ~/paintstorm" + echo "" + echo "Option B: Use the wrapper script:" + echo " ./fix-pointer-freeze.sh and create the wrapper" + echo "" + echo "Option C: Just use native HID (stop OTD):" + echo " pkill otd-daemon" + echo " xinput enable 11 && xinput enable 24" + echo " ~/paintstorm" + fi +else + echo -e "${GREEN}✓ Device states are consistent${NC}" + echo "" + echo "The native device state didn't change between launches." + echo "The problem might be something else." + echo "" + echo "Possible causes:" + echo "- Paintstorm is caching device preferences" + echo "- X11 input focus issues" + echo "- Application-level device selection" + echo "" + echo "Try: Clear Paintstorm's preferences/cache and test again" +fi + +echo "" +echo "==========================================" +echo "NEXT STEPS" +echo "==========================================" +echo "" + +echo "1. Run this test again to confirm the behavior" +echo "" +echo "2. If native devices are being re-enabled:" +echo " Create a wrapper script that force-disables them:" +echo "" +echo " cat > ~/paintstorm-safe << 'EOF'" +echo " #!/bin/bash" +echo " xinput disable 11 2>/dev/null || true" +echo " xinput disable 24 2>/dev/null || true" +echo " ~/paintstorm" +echo " EOF" +echo " chmod +x ~/paintstorm-safe" +echo "" +echo "3. Or just use native HID (simpler):" +echo " pkill otd-daemon" +echo " ~/paintstorm" +echo "" @@ -0,0 +1,236 @@ +#!/bin/bash +# Track device state changes before/after launching Paintstorm + +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +BOLD='\033[1m' +NC='\033[0m' + +SNAPSHOT_DIR="/tmp/device-snapshots" +mkdir -p "$SNAPSHOT_DIR" + +take_snapshot() { + local label="$1" + local timestamp=$(date +%s) + local file="$SNAPSHOT_DIR/${label}_${timestamp}.txt" + + echo "=== Device Snapshot: $label ===" > "$file" + echo "Time: $(date)" >> "$file" + echo "" >> "$file" + + echo ">>> xinput list" >> "$file" + xinput list >> "$file" 2>&1 + echo "" >> "$file" + + echo ">>> xinput list (devices only)" >> "$file" + xinput list | grep -E "(HUION|OpenTabletDriver|Virtual core pointer|pointer|stylus|eraser)" >> "$file" 2>&1 + echo "" >> "$file" + + echo ">>> Disabled devices (floating)" >> "$file" + xinput list | grep "floating" >> "$file" 2>&1 + echo "" >> "$file" + + echo ">>> OTD daemon status" >> "$file" + pgrep -fa otd-daemon >> "$file" 2>&1 || echo "Not running" >> "$file" + echo "" >> "$file" + + echo ">>> Native device states" >> "$file" + for id in 11 24; do + if xinput list | grep -q "id=$id"; then + state=$(xinput list | grep "id=$id" | grep -o '\[.*\]') + name=$(xinput list | grep "id=$id" | sed 's/.*↳ //' | sed 's/id=.*//' | xargs) + echo " id=$id ($name): $state" >> "$file" + fi + done + echo "" >> "$file" + + echo ">>> Virtual device states" >> "$file" + xinput list | grep "OpenTabletDriver" | while read line; do + id=$(echo "$line" | grep -o 'id=[0-9]*' | cut -d= -f2) + state=$(echo "$line" | grep -o '\[.*\]') + name=$(echo "$line" | sed 's/.*↳ //' | sed 's/id=.*//' | xargs) + echo " id=$id ($name): $state" >> "$file" + done >> "$file" 2>&1 + echo "" >> "$file" + + # Test which devices are active + echo ">>> Testing native device (id=11) for 2 seconds" >> "$file" + if xinput list | grep -q "id=11"; then + native_events=$(timeout 2 xinput test 11 2>&1 | grep -c "motion" || echo "0") + echo " Events detected: $native_events" >> "$file" + else + echo " Device not found" >> "$file" + fi + echo "" >> "$file" + + echo ">>> Testing OTD virtual device for 2 seconds" >> "$file" + virtual_id=$(xinput list | grep "OpenTabletDriver.*stylus" | head -1 | grep -o 'id=[0-9]*' | cut -d= -f2) + if [ -n "$virtual_id" ]; then + virtual_events=$(timeout 2 xinput test "$virtual_id" 2>&1 | grep -c "motion" || echo "0") + echo " Virtual device id=$virtual_id" >> "$file" + echo " Events detected: $virtual_events" >> "$file" + else + echo " Device not found" >> "$file" + fi + + echo -e "${GREEN}✓ Snapshot saved: $file${NC}" + echo "$file" +} + +compare_snapshots() { + echo "" + echo "==========================================" + echo "Comparing Snapshots" + echo "==========================================" + echo "" + + local files=($(ls -t "$SNAPSHOT_DIR"/*.txt 2>/dev/null)) + + if [ ${#files[@]} -lt 2 ]; then + echo -e "${YELLOW}Need at least 2 snapshots to compare${NC}" + return + fi + + echo "Latest snapshots:" + for i in "${!files[@]}"; do + basename="${files[$i]##*/}" + label=$(echo "$basename" | sed 's/_[0-9]*\.txt//') + timestamp=$(stat -c %y "${files[$i]}" | cut -d' ' -f1-2 | cut -d. -f1) + echo " [$i] $label - $timestamp" + done + echo "" + + read -p "Compare which two? (e.g., 0 1): " idx1 idx2 + + if [ -f "${files[$idx1]}" ] && [ -f "${files[$idx2]}" ]; then + echo "" + echo "Differences between snapshots:" + echo "" + diff -u "${files[$idx1]}" "${files[$idx2]}" || true + else + echo -e "${RED}Invalid selection${NC}" + fi +} + +show_all_snapshots() { + echo "" + echo "==========================================" + echo "All Snapshots" + echo "==========================================" + echo "" + + local files=($(ls -t "$SNAPSHOT_DIR"/*.txt 2>/dev/null)) + + if [ ${#files[@]} -eq 0 ]; then + echo -e "${YELLOW}No snapshots found${NC}" + return + fi + + for file in "${files[@]}"; do + basename="${file##*/}" + label=$(echo "$basename" | sed 's/_[0-9]*\.txt//') + timestamp=$(stat -c %y "$file" | cut -d' ' -f1-2 | cut -d. -f1) + + echo -e "${BOLD}--- $label ($timestamp) ---${NC}" + + # Show key info + echo "OTD: $(grep -A 1 "OTD daemon status" "$file" | tail -1)" + echo "Native id=11: $(grep "id=11" "$file" | grep -o '\[.*\]' | head -1)" + echo "Native id=24: $(grep "id=24" "$file" | grep -o '\[.*\]' | head -1)" + + virtual_line=$(grep "OpenTabletDriver.*stylus" "$file" | head -1) + if [ -n "$virtual_line" ]; then + virtual_state=$(echo "$virtual_line" | grep -o '\[.*\]') + echo "Virtual device: $virtual_state" + else + echo "Virtual device: NOT FOUND" + fi + + echo "" + done +} + +echo "==========================================" +echo "Device State Tracker" +echo "==========================================" +echo "" +echo "This tool tracks xinput device states to diagnose" +echo "why Paintstorm works the first time but not the second." +echo "" +echo "What to do:" +echo "1) Take snapshot: Before starting OTD" +echo "2) Start OTD, take snapshot: After OTD starts" +echo "3) Take snapshot: After disabling native devices" +echo "4) Launch Paintstorm (1st time), take snapshot: After first launch" +echo "5) Close Paintstorm, take snapshot: After closing" +echo "6) Launch Paintstorm (2nd time), take snapshot: After second launch" +echo "7) Compare snapshots to see what changed" +echo "" +echo "==========================================" +echo "" + +echo "Choose an action:" +echo "" +echo "1) Take snapshot (with label)" +echo "2) Quick snapshot (auto-label with current state)" +echo "3) Show all snapshots" +echo "4) Compare two snapshots" +echo "5) Clear all snapshots" +echo "6) Exit" +echo "" +read -p "Choice [1-6]: " choice + +case $choice in + 1) + echo "" + read -p "Enter label for this snapshot: " label + label=$(echo "$label" | tr ' ' '_') + take_snapshot "$label" + ;; + + 2) + echo "" + # Auto-detect state + otd_running=$(pgrep -f otd-daemon > /dev/null && echo "otd_running" || echo "otd_stopped") + native_disabled=$(xinput list | grep "id=11" | grep -q "floating" && echo "native_disabled" || echo "native_enabled") + + auto_label="${otd_running}_${native_disabled}" + echo "Auto-detected state: $auto_label" + take_snapshot "$auto_label" + ;; + + 3) + show_all_snapshots + ;; + + 4) + compare_snapshots + ;; + + 5) + echo "" + read -p "Delete all snapshots? [y/N]: " confirm + if [[ "$confirm" =~ ^[Yy]$ ]]; then + rm -f "$SNAPSHOT_DIR"/*.txt + echo -e "${GREEN}All snapshots cleared${NC}" + else + echo "Cancelled" + fi + ;; + + 6) + echo "Exiting." + exit 0 + ;; + + *) + echo "Invalid choice" + exit 1 + ;; +esac + +echo "" +echo "Snapshots are stored in: $SNAPSHOT_DIR" +echo ""