227 lines
6.2 KiB
JavaScript
227 lines
6.2 KiB
JavaScript
const express = require('express');
|
|
const bodyParser = require('body-parser');
|
|
const { spawn, execSync, exec } = require('child_process');
|
|
const fs = require('fs');
|
|
|
|
const app = express();
|
|
app.use(bodyParser.urlencoded({ extended: true }));
|
|
app.use(bodyParser.json());
|
|
|
|
const PORT = process.env.PORT || 5011;
|
|
const MONITOR = process.env.MONITOR || 'HDMI-1';
|
|
|
|
// State variables
|
|
let browserProcess = null;
|
|
let autorefreshInterval = null;
|
|
|
|
let currentUrl = process.env.LAUNCH_URL || 'https://duckduckgo.com';
|
|
let isKiosk = 1; // Cog is always full-screen/kiosk implicitly
|
|
let isGpu = 1; // WPE is inherently hardware-accelerated
|
|
|
|
function getMonitorGeometry(monitorName) {
|
|
try {
|
|
const stdout = execSync('xrandr').toString();
|
|
const lines = stdout.split('\n');
|
|
for (const line of lines) {
|
|
if (line.startsWith(monitorName + ' ') && line.includes(' connected')) {
|
|
const match = line.match(/(\d+)x(\d+)\+(\d+)\+(\d+)/);
|
|
if (match) {
|
|
return {
|
|
width: match[1],
|
|
height: match[2],
|
|
x: match[3],
|
|
y: match[4]
|
|
};
|
|
}
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.error('Failed to execute xrandr:', e.message);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
function ensureMonitorConnected() {
|
|
return new Promise(resolve => {
|
|
const check = () => {
|
|
try {
|
|
execSync('xrandr --auto');
|
|
} catch(e) {}
|
|
|
|
const geom = getMonitorGeometry(MONITOR);
|
|
if (geom) {
|
|
console.log(`Monitor ${MONITOR} is connected and ready.`);
|
|
resolve(geom);
|
|
} else {
|
|
console.log(`Waiting for monitor ${MONITOR} to be connected...`);
|
|
setTimeout(check, 5000);
|
|
}
|
|
};
|
|
check();
|
|
});
|
|
}
|
|
|
|
function positionCogWindow(geometry) {
|
|
// Wait for the window to appear, then use wmctrl to move it
|
|
console.log('Attempting to position Cog window...');
|
|
let attempts = 10;
|
|
const tryMove = () => {
|
|
try {
|
|
// Find the window ID of the Cog browser.
|
|
// Cog's window title or class is usually 'cog'
|
|
const stdout = execSync('wmctrl -l').toString();
|
|
if (stdout.toLowerCase().includes('cog')) {
|
|
// Find WID
|
|
const line = stdout.split('\n').find(l => l.toLowerCase().includes('cog'));
|
|
if (line) {
|
|
const wid = line.split(' ')[0];
|
|
console.log(`Found Cog window: ${wid}. Positioning to ${geometry.x},${geometry.y}...`);
|
|
// Remove maximization first
|
|
try { execSync(`wmctrl -i -r ${wid} -b remove,maximized_vert,maximized_horz`); } catch (e) {}
|
|
// Move and resize: gravity,x,y,w,h
|
|
execSync(`wmctrl -i -r ${wid} -e 0,${geometry.x},${geometry.y},${geometry.width},${geometry.height}`);
|
|
console.log('Window positioned successfully.');
|
|
return; // Done
|
|
}
|
|
}
|
|
} catch (e) {
|
|
console.log('wmctrl check failed', e.message);
|
|
}
|
|
|
|
attempts--;
|
|
if (attempts > 0) {
|
|
setTimeout(tryMove, 1000);
|
|
} else {
|
|
console.error('Failed to find and position Cog window.');
|
|
}
|
|
};
|
|
|
|
setTimeout(tryMove, 2000);
|
|
}
|
|
|
|
async function startBrowser() {
|
|
console.log(`Checking connection status for ${MONITOR}...`);
|
|
const geometry = await ensureMonitorConnected();
|
|
|
|
if (browserProcess) {
|
|
console.log('Stopping existing Cog...');
|
|
browserProcess.kill('SIGTERM');
|
|
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
}
|
|
|
|
console.log(`Starting Cog to navigate to ${currentUrl}`);
|
|
|
|
// Launch Cog. We use the X11 platform if available.
|
|
const args = [currentUrl];
|
|
const env = Object.assign({}, process.env, {
|
|
COG_PLATFORM_NAME: 'x11',
|
|
// Ensures background opacity might work and standard behaviors
|
|
});
|
|
|
|
browserProcess = spawn('cog', args, {
|
|
stdio: 'inherit',
|
|
env: env
|
|
});
|
|
|
|
// Call position function which will wait for window to exist and move it
|
|
positionCogWindow(geometry);
|
|
}
|
|
|
|
// API Endpoints
|
|
|
|
app.get('/ping', (req, res) => {
|
|
res.status(200).send('OK');
|
|
});
|
|
|
|
app.post('/refresh', (req, res) => {
|
|
try {
|
|
execSync('cogctl reload');
|
|
res.status(200).send('Refreshed');
|
|
} catch (e) {
|
|
res.status(503).send('Browser not ready');
|
|
}
|
|
});
|
|
|
|
app.post('/autorefresh/:interval', (req, res) => {
|
|
const interval = parseInt(req.params.interval, 10);
|
|
if (autorefreshInterval) {
|
|
clearInterval(autorefreshInterval);
|
|
autorefreshInterval = null;
|
|
}
|
|
|
|
if (interval > 0 && interval <= 60) {
|
|
autorefreshInterval = setInterval(() => {
|
|
try { execSync('cogctl reload'); } catch(e) {}
|
|
}, interval * 1000);
|
|
res.status(200).send(`Autorefresh enabled: ${interval}s`);
|
|
} else {
|
|
res.status(200).send('Autorefresh disabled');
|
|
}
|
|
});
|
|
|
|
app.post('/scan', (req, res) => {
|
|
res.status(200).send('Scanned');
|
|
});
|
|
|
|
app.get('/url', (req, res) => {
|
|
res.status(200).send(currentUrl);
|
|
});
|
|
|
|
app.post('/url', (req, res) => {
|
|
if (req.body.url) currentUrl = req.body.url;
|
|
|
|
// Cogctl can directly tell the browser to open a URL without restarting it
|
|
try {
|
|
console.log(`Sending cogctl open ${currentUrl}`);
|
|
execSync(`cogctl open "${currentUrl}"`);
|
|
res.status(200).send(`URL set to ${currentUrl}`);
|
|
} catch (e) {
|
|
console.error('Failed to set URL via cogctl. Attempting restart...', e.message);
|
|
startBrowser().then(() => {
|
|
res.status(200).send(`URL set to ${currentUrl} (restarted)`);
|
|
});
|
|
}
|
|
});
|
|
|
|
app.get('/gpu', (req, res) => {
|
|
res.status(200).send('1'); // Always 1 for WPE
|
|
});
|
|
|
|
app.put('/gpu/:value', (req, res) => {
|
|
res.status(200).send('1'); // Ignored for WPE
|
|
});
|
|
|
|
app.get('/kiosk', (req, res) => {
|
|
res.status(200).send('1'); // Always 1 for WPE
|
|
});
|
|
|
|
app.put('/kiosk/:value', (req, res) => {
|
|
res.status(200).send('1'); // Ignored for WPE
|
|
});
|
|
|
|
app.get('/flags', (req, res) => {
|
|
res.status(200).json(['WPE WebKit (Cog)']);
|
|
});
|
|
|
|
app.get('/version', (req, res) => {
|
|
exec('cog --version', (err, stdout) => {
|
|
if (err) res.status(500).send('Unknown');
|
|
else res.status(200).send(stdout.trim());
|
|
});
|
|
});
|
|
|
|
app.get('/screenshot', (req, res) => {
|
|
const file = `/tmp/screenshot-${Date.now()}.png`;
|
|
exec(`scrot ${file}`, (err) => {
|
|
if (err) return res.status(500).send('Screenshot failed');
|
|
res.sendFile(file);
|
|
});
|
|
});
|
|
|
|
// Start initially
|
|
startBrowser();
|
|
|
|
app.listen(PORT, () => {
|
|
console.log(`Kiosk API running on port ${PORT} for monitor ${MONITOR}`);
|
|
});
|