#!/usr/bin/env node import { readdirSync, readFileSync, readlinkSync } from 'fs'; import { execFileSync } from 'child_process'; const window_id = process.argv[2]; if (!window_id) { console.error('Usage: debug-window.js '); process.exit(1); } function read_file(path) { try { return readFileSync(path, 'utf8'); } catch { return null; } } function get_ppid(pid) { const status = read_file(`/proc/${pid}/status`); const match = status?.match(/^PPid:\s+(\d+)/m); return match ? match[1] : null; } function get_comm(pid) { return read_file(`/proc/${pid}/comm`)?.trim() ?? null; } function get_cmdline(pid) { return read_file(`/proc/${pid}/cmdline`)?.replace(/\0/g, ' ').trim() ?? null; } // Get PID of the window const out = execFileSync('xprop', ['-id', window_id, '_NET_WM_PID'], { encoding: 'utf8' }); const match = out.match(/= (\d+)/); if (!match) { console.error('Could not get PID for window', window_id); process.exit(1); } const root_pid = match[1]; console.log(`Window ${window_id} → PID ${root_pid} (${get_comm(root_pid)})`); // Find all descendants const children = new Map(); // pid → [child pids] for (const entry of readdirSync('/proc')) { if (!/^\d+$/.test(entry)) continue; const ppid = get_ppid(entry); if (!ppid) continue; if (!children.has(ppid)) children.set(ppid, []); children.get(ppid).push(entry); } function print_tree(pid, depth = 0) { const indent = ' '.repeat(depth); const comm = get_comm(pid) ?? '?'; const cmdline = get_cmdline(pid) ?? ''; console.log(`${indent}${pid} [${comm}] ${cmdline.slice(0, 120)}`); for (const child of children.get(pid) ?? []) { print_tree(child, depth + 1); } } print_tree(root_pid);