Add Linux support with application mute handling and startup script
This commit is contained in:
36
README.md
36
README.md
@@ -27,10 +27,40 @@ The architecture consists of three main components:
|
|||||||
|
|
||||||
## Starting the Server
|
## Starting the Server
|
||||||
|
|
||||||
There are a couple of ways to launch the software depending on your needs:
|
There are a few ways to launch the software depending on your needs:
|
||||||
|
|
||||||
- **Interactive Mode (`Start-Macropad.bat`)**: Starts the Node.js server, displays the console output, and automatically opens your default web browser to the `http://localhost:3001` configuration page.
|
- **Interactive Mode (Windows) (`Start-Macropad.bat`)**: Starts the Node.js server, displays the console output, and automatically opens your default web browser to the `http://localhost:3001` configuration page.
|
||||||
- **Background Mode (`Run-Hidden.vbs`)**: Silently launches the Node server in the background without opening a visible command prompt window. This is ideal for adding to your Windows Startup folder for seamless background operation.
|
- **Background Mode (Windows) (`Run-Hidden.vbs`)**: Silently launches the Node server in the background without opening a visible command prompt window. This is ideal for adding to your Windows Startup folder for seamless background operation.
|
||||||
|
- **Linux Mode (`start-macropad.sh`)**: Starts the Node.js server and automatically opens the configuration page in your default browser.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Linux Installation & Prerequisites
|
||||||
|
|
||||||
|
To run the application on Linux (e.g., Ubuntu/Debian), perform the following configuration steps:
|
||||||
|
|
||||||
|
### 1. Serial Port Permissions
|
||||||
|
By default, Linux limits access to serial devices (like `/dev/ttyACM0`). Give your user account permission to access the Pico serial port by running:
|
||||||
|
```bash
|
||||||
|
sudo usermod -a -G dialout $USER
|
||||||
|
```
|
||||||
|
*Note: You must log out and log back in (or reboot) for this change to take effect.*
|
||||||
|
|
||||||
|
### 2. Keyboard Simulation Dependencies (`nut-js`)
|
||||||
|
The keyboard simulation library requires the development header for `libXtst` and requires an **X11 / XWayland** environment (native Wayland will block synthetic input):
|
||||||
|
```bash
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install libxtst-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Launching
|
||||||
|
Make the script executable and run it:
|
||||||
|
```bash
|
||||||
|
chmod +x start-macropad.sh
|
||||||
|
./start-macropad.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
|
|||||||
40
server.js
40
server.js
@@ -148,6 +148,10 @@ async function connectToPico() {
|
|||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
serialPort.on('error', (err) => {
|
||||||
|
console.error('Serial port error: ', err.message);
|
||||||
|
});
|
||||||
|
|
||||||
parser = serialPort.pipe(new ReadlineParser({ delimiter: '\n' }));
|
parser = serialPort.pipe(new ReadlineParser({ delimiter: '\n' }));
|
||||||
|
|
||||||
parser.on('data', async (line) => {
|
parser.on('data', async (line) => {
|
||||||
@@ -193,10 +197,46 @@ async function connectToPico() {
|
|||||||
});
|
});
|
||||||
} else if (btn.actionType === "mute_app" && btn.cmd) {
|
} else if (btn.actionType === "mute_app" && btn.cmd) {
|
||||||
console.log(`Toggling mute for: ${btn.cmd}`);
|
console.log(`Toggling mute for: ${btn.cmd}`);
|
||||||
|
if (process.platform === 'win32') {
|
||||||
const svvPath = path.join(__dirname, 'SoundVolumeView.exe');
|
const svvPath = path.join(__dirname, 'SoundVolumeView.exe');
|
||||||
exec(`"${svvPath}" /Switch "${btn.cmd}"`, (error, stdout, stderr) => {
|
exec(`"${svvPath}" /Switch "${btn.cmd}"`, (error, stdout, stderr) => {
|
||||||
if (error) console.error(`Mute error: ${error}\n${stderr}`);
|
if (error) console.error(`Mute error: ${error}\n${stderr}`);
|
||||||
});
|
});
|
||||||
|
} else if (process.platform === 'linux') {
|
||||||
|
const cleanAppName = btn.cmd.replace(/\.exe$/i, '').toLowerCase();
|
||||||
|
exec('pactl list sink-inputs', (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
console.error(`pactl error: ${error}\n${stderr}`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const blocks = stdout.split(/Sink Input #/i);
|
||||||
|
let found = false;
|
||||||
|
for (const block of blocks) {
|
||||||
|
if (!block.trim()) continue;
|
||||||
|
const lines = block.split('\n');
|
||||||
|
const index = parseInt(lines[0].trim());
|
||||||
|
if (isNaN(index)) continue;
|
||||||
|
const blockLower = block.toLowerCase();
|
||||||
|
const hasAppName = blockLower.includes(`application.name = "${cleanAppName}"`) ||
|
||||||
|
blockLower.includes(`application.name = '${cleanAppName}'`) ||
|
||||||
|
blockLower.includes(`application.process.binary = "${cleanAppName}"`) ||
|
||||||
|
blockLower.includes(`application.process.binary = '${cleanAppName}'`) ||
|
||||||
|
blockLower.includes(`media.name = "${cleanAppName}"`);
|
||||||
|
if (hasAppName) {
|
||||||
|
found = true;
|
||||||
|
console.log(`Found stream index ${index} for ${cleanAppName}, toggling mute...`);
|
||||||
|
exec(`pactl set-sink-input-mute ${index} toggle`, (err) => {
|
||||||
|
if (err) console.error(`Failed to mute sink input ${index}: ${err}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
console.log(`Application '${cleanAppName}' not found in active audio streams.`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.warn(`mute_app is not supported on platform: ${process.platform}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (line.startsWith("R,")) {
|
} else if (line.startsWith("R,")) {
|
||||||
|
|||||||
22
start-macropad.sh
Normal file
22
start-macropad.sh
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Move to the script's directory
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
|
||||||
|
echo "Starting Macropad Control Center..."
|
||||||
|
node server.js &
|
||||||
|
SERVER_PID=$!
|
||||||
|
|
||||||
|
# Wait 2 seconds for server to start
|
||||||
|
sleep 2
|
||||||
|
|
||||||
|
# Open local config page in default browser
|
||||||
|
if command -v xdg-open > /dev/null; then
|
||||||
|
xdg-open http://localhost:3001
|
||||||
|
elif command -v gnome-open > /dev/null; then
|
||||||
|
gnome-open http://localhost:3001
|
||||||
|
else
|
||||||
|
echo "Please open http://localhost:3001 in your browser"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Keep the script running to allow stopping the server with Ctrl+C
|
||||||
|
wait $SERVER_PID
|
||||||
Reference in New Issue
Block a user