diff --git a/README.md b/README.md index a1129bc..0be087f 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,55 @@ sudo apt install xdotool - Whip him 😩💢 - It sends an interrupt (Ctrl-C) and one of 5 encouraging messages! +## Linux Setup + +### X11 + +```bash +# Debian/Ubuntu +sudo apt install xdotool + +# Arch +sudo pacman -S xdotool +``` + +### Wayland (GNOME, etc.) + +```bash +# Arch +sudo pacman -S ydotool + +# Debian/Ubuntu +sudo apt install ydotool +``` + +Enable and start the ydotool daemon: + +```bash +sudo systemctl enable --now ydotool +sudo usermod -aG input $USER +``` + +Log out and back in for the group change to take effect. + +### GNOME Tray Icon + +GNOME doesn't show tray icons by default. Install the AppIndicator extension: + +```bash +# Arch +sudo pacman -S gnome-shell-extension-appindicator + +# Debian/Ubuntu +sudo apt install gnome-shell-extension-appindicator +``` + +Enable it via GNOME Extensions app or: + +```bash +gnome-extensions enable appindicatorsupport@rgcjonas.gmail.com +``` + ## Roadmap - [x] Initial release! 🥳 diff --git a/bin/badclaude.js b/bin/badclaude.js old mode 100644 new mode 100755 diff --git a/main.js b/main.js index 1360458..e1e9c50 100644 --- a/main.js +++ b/main.js @@ -53,11 +53,18 @@ function refocusPreviousApp() { } }); } else if (process.platform === 'linux') { - execFile('xdotool', ['key', '--clearmodifiers', 'alt+Tab'], err => { - if (err) { - console.warn('refocus previous app (Alt+Tab) failed. Install xdotool:', err.message); - } - }); + if (process.env.XDG_SESSION_TYPE === 'wayland') { + // ydotool scancodes: 56=Alt, 15=Tab + execFile('ydotool', ['key', '56:1', '15:1', '15:0', '56:0'], err => { + if (err) console.warn('refocus (ydotool Alt+Tab) failed:', err.message); + }); + } else { + execFile('xdotool', ['key', '--clearmodifiers', 'alt+Tab'], err => { + if (err) { + console.warn('refocus previous app (Alt+Tab) failed. Install xdotool:', err.message); + } + }); + } } }; setTimeout(run, delayMs); @@ -122,7 +129,9 @@ async function getTrayIcon() { // ── Overlay window ────────────────────────────────────────────────────────── function createOverlay() { - const { bounds } = screen.getPrimaryDisplay(); + const cursor = screen.getCursorScreenPoint(); + const display = screen.getDisplayNearestPoint(cursor); + const { bounds } = display; overlay = new BrowserWindow({ x: bounds.x, y: bounds.y, width: bounds.width, height: bounds.height, @@ -260,19 +269,35 @@ function sendMacroMac(text) { } function sendMacroLinux(text) { - execFile( - 'xdotool', - [ - 'key', '--clearmodifiers', 'ctrl+c', - 'type', '--delay', '1', '--clearmodifiers', '--', text, - 'key', 'Return', - ], - err => { + if (process.env.XDG_SESSION_TYPE === 'wayland') { + // ydotool uses uinput — requires ydotoold service and user in 'input' group + // Scancodes: 29=LCtrl, 46=C, 28=Enter + execFile('ydotool', ['key', '29:1', '46:1', '46:0', '29:0'], err => { if (err) { - console.warn('linux macro failed. Install xdotool:', err.message); + console.warn('ydotool failed (ensure ydotoold is running and user is in input group):', err.message); + return; } - } - ); + setTimeout(() => { + execFile('ydotool', ['type', '--', text], () => { + execFile('ydotool', ['key', '28:1', '28:0']); + }); + }, 30); + }); + } else { + execFile( + 'xdotool', + [ + 'key', '--clearmodifiers', 'ctrl+c', + 'type', '--delay', '1', '--clearmodifiers', '--', text, + 'key', 'Return', + ], + err => { + if (err) { + console.warn('linux macro failed. Install xdotool:', err.message); + } + } + ); + } } // ── App lifecycle ───────────────────────────────────────────────────────────