refactor(*): move to x11
This commit is contained in:
@@ -1,23 +1,19 @@
|
|||||||
FROM debian:bullseye-slim
|
FROM debian:bullseye-slim
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
sway \
|
xserver-xorg-core \
|
||||||
xwayland \
|
xserver-xorg-video-fbdev \
|
||||||
dbus-x11 \
|
xserver-xorg-video-fbturbo \
|
||||||
libwayland-client0 \
|
xserver-xorg-video-all \
|
||||||
libwayland-server0 \
|
|
||||||
libwayland-egl1 \
|
|
||||||
mesa-vulkan-drivers \
|
|
||||||
libvulkan1 \
|
|
||||||
fonts-liberation \
|
|
||||||
x11-xserver-utils \
|
x11-xserver-utils \
|
||||||
|
xinit \
|
||||||
|
dbus-x11 \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
COPY sway.conf /etc/sway/config
|
|
||||||
COPY start.sh /start.sh
|
COPY start.sh /start.sh
|
||||||
RUN chmod +x /start.sh
|
RUN chmod +x /start.sh
|
||||||
|
|
||||||
# Need to run as root to access DRM devices on many kiosk setups
|
# Xorg needs root to access DRM and VT
|
||||||
USER root
|
USER root
|
||||||
|
|
||||||
CMD ["/start.sh"]
|
CMD ["/start.sh"]
|
||||||
|
|||||||
@@ -1,16 +1,12 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Ensure runtime directory exists
|
# Ensure X11 socket directory exists
|
||||||
mkdir -p /run/wayland
|
mkdir -p /tmp/.X11-unix
|
||||||
chmod 0700 /run/wayland
|
chmod 1777 /tmp/.X11-unix
|
||||||
|
|
||||||
# Set env vars for Wayland/Sway
|
echo "Starting Xorg server..."
|
||||||
export XDG_RUNTIME_DIR=/run/wayland
|
# Run Xorg on DISPLAY :0.
|
||||||
export WAYLAND_DISPLAY=wayland-1
|
# We disable DPMS (screen blanking) and screen saver (-s 0).
|
||||||
export WLR_BACKENDS=drm,libinput
|
# We also avoid requiring a virtual terminal using vt7 and -sharevts if needed,
|
||||||
# Fallback to headless if DRM is not available (useful for testing on mac/desktop without actual DRM)
|
# though running as root with devices mapped usually succeeds.
|
||||||
# export WLR_BACKENDS=headless
|
exec Xorg :0 -nocursor -s 0 -dpms vt7
|
||||||
|
|
||||||
# Run sway
|
|
||||||
echo "Starting Sway..."
|
|
||||||
exec sway -d -c /etc/sway/config
|
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
# Default sway config for Kiosk
|
|
||||||
|
|
||||||
# Disable default bar
|
|
||||||
bar {
|
|
||||||
mode invisible
|
|
||||||
}
|
|
||||||
|
|
||||||
# Remove window borders and title bars
|
|
||||||
default_border none
|
|
||||||
default_floating_border none
|
|
||||||
hide_edge_borders both
|
|
||||||
|
|
||||||
# Map workspaces to specific outputs
|
|
||||||
# Note: In a real Pi 4 environment, the outputs might be named HDMI-A-1 and HDMI-A-2.
|
|
||||||
# You can customize these output names based on `swaymsg -t get_outputs`.
|
|
||||||
workspace 1 output HDMI-A-1
|
|
||||||
workspace 2 output HDMI-A-2
|
|
||||||
|
|
||||||
# Set a black background
|
|
||||||
output * bg #000000 solid_color
|
|
||||||
|
|
||||||
# Idle configuration
|
|
||||||
# We don't want the screen to turn off in a kiosk.
|
|
||||||
exec swayidle -w \
|
|
||||||
timeout 3600 'swaymsg "output * dpms on"' \
|
|
||||||
resume 'swaymsg "output * dpms on"'
|
|
||||||
|
|
||||||
# Include any other specific configurations
|
|
||||||
include /etc/sway/config.d/*
|
|
||||||
+14
-14
@@ -5,12 +5,16 @@ services:
|
|||||||
build: ./display-manager
|
build: ./display-manager
|
||||||
privileged: true
|
privileged: true
|
||||||
restart: always
|
restart: always
|
||||||
|
tty: true
|
||||||
|
stdin_open: true
|
||||||
volumes:
|
volumes:
|
||||||
- wayland-socket:/run/wayland
|
- x11-socket:/tmp/.X11-unix
|
||||||
- /dev:/dev
|
- /dev:/dev
|
||||||
|
devices:
|
||||||
|
- "/dev/tty7:/dev/tty7"
|
||||||
|
- "/dev/dri:/dev/dri"
|
||||||
environment:
|
environment:
|
||||||
- XDG_RUNTIME_DIR=/run/wayland
|
- DISPLAY=:0
|
||||||
- WAYLAND_DISPLAY=wayland-1
|
|
||||||
|
|
||||||
kiosk-1:
|
kiosk-1:
|
||||||
build: ./kiosk
|
build: ./kiosk
|
||||||
@@ -20,12 +24,10 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "5011:5011"
|
- "5011:5011"
|
||||||
volumes:
|
volumes:
|
||||||
- wayland-socket:/run/wayland
|
- x11-socket:/tmp/.X11-unix
|
||||||
environment:
|
environment:
|
||||||
- XDG_RUNTIME_DIR=/run/wayland
|
- DISPLAY=:0
|
||||||
- WAYLAND_DISPLAY=wayland-1
|
- MONITOR=HDMI-1
|
||||||
- SWAYSOCK=/run/wayland/sway-ipc.sock
|
|
||||||
- WORKSPACE=1
|
|
||||||
- PORT=5011
|
- PORT=5011
|
||||||
- LAUNCH_URL=https://github.com
|
- LAUNCH_URL=https://github.com
|
||||||
- KIOSK=1
|
- KIOSK=1
|
||||||
@@ -39,16 +41,14 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "5012:5012"
|
- "5012:5012"
|
||||||
volumes:
|
volumes:
|
||||||
- wayland-socket:/run/wayland
|
- x11-socket:/tmp/.X11-unix
|
||||||
environment:
|
environment:
|
||||||
- XDG_RUNTIME_DIR=/run/wayland
|
- DISPLAY=:0
|
||||||
- WAYLAND_DISPLAY=wayland-1
|
- MONITOR=HDMI-2
|
||||||
- SWAYSOCK=/run/wayland/sway-ipc.sock
|
|
||||||
- WORKSPACE=2
|
|
||||||
- PORT=5012
|
- PORT=5012
|
||||||
- LAUNCH_URL=https://google.com
|
- LAUNCH_URL=https://google.com
|
||||||
- KIOSK=1
|
- KIOSK=1
|
||||||
- GPU=1
|
- GPU=1
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
wayland-socket:
|
x11-socket:
|
||||||
|
|||||||
+3
-3
@@ -1,10 +1,10 @@
|
|||||||
FROM node:18-bullseye-slim
|
FROM node:18-bullseye-slim
|
||||||
|
|
||||||
# Install dependencies for Chromium, Wayland, and screenshot tool
|
# Install dependencies for Chromium and X11
|
||||||
RUN apt-get update && apt-get install -y \
|
RUN apt-get update && apt-get install -y \
|
||||||
chromium \
|
chromium \
|
||||||
sway \
|
x11-xserver-utils \
|
||||||
grim \
|
scrot \
|
||||||
jq \
|
jq \
|
||||||
curl \
|
curl \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|||||||
+52
-35
@@ -1,7 +1,7 @@
|
|||||||
const express = require('express');
|
const express = require('express');
|
||||||
const bodyParser = require('body-parser');
|
const bodyParser = require('body-parser');
|
||||||
const puppeteer = require('puppeteer-core');
|
const puppeteer = require('puppeteer-core');
|
||||||
const { spawn, exec } = require('child_process');
|
const { spawn, exec, execSync } = require('child_process');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ app.use(bodyParser.urlencoded({ extended: true }));
|
|||||||
app.use(bodyParser.json());
|
app.use(bodyParser.json());
|
||||||
|
|
||||||
const PORT = process.env.PORT || 5011;
|
const PORT = process.env.PORT || 5011;
|
||||||
const WORKSPACE = process.env.WORKSPACE || '1';
|
const MONITOR = process.env.MONITOR || 'HDMI-1';
|
||||||
|
|
||||||
// State variables
|
// State variables
|
||||||
let browserProcess = null;
|
let browserProcess = null;
|
||||||
@@ -23,19 +23,48 @@ let isKiosk = process.env.KIOSK !== '0'; // default 1
|
|||||||
let isGpu = process.env.GPU !== '0'; // default 1
|
let isGpu = process.env.GPU !== '0'; // default 1
|
||||||
let chromiumFlags = [];
|
let chromiumFlags = [];
|
||||||
|
|
||||||
|
function getMonitorGeometry(monitorName) {
|
||||||
|
try {
|
||||||
|
const stdout = execSync('xrandr').toString();
|
||||||
|
const lines = stdout.split('\n');
|
||||||
|
for (const line of lines) {
|
||||||
|
if (line.includes(monitorName) && line.includes('connected')) {
|
||||||
|
// Match geometry string like 1920x1080+1920+0
|
||||||
|
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 parse xrandr:', e);
|
||||||
|
}
|
||||||
|
// Fallback geometry if xrandr fails
|
||||||
|
return { width: 1920, height: 1080, x: 0, y: 0 };
|
||||||
|
}
|
||||||
|
|
||||||
function buildFlags() {
|
function buildFlags() {
|
||||||
|
const geometry = getMonitorGeometry(MONITOR);
|
||||||
|
console.log(`Monitor ${MONITOR} resolved to position ${geometry.x},${geometry.y} with size ${geometry.width}x${geometry.height}`);
|
||||||
|
|
||||||
const flags = [
|
const flags = [
|
||||||
'--no-sandbox',
|
'--no-sandbox',
|
||||||
'--disable-dev-shm-usage',
|
'--disable-dev-shm-usage',
|
||||||
'--remote-debugging-port=9222',
|
'--remote-debugging-port=9222',
|
||||||
'--enable-features=UseOzonePlatform',
|
`--user-data-dir=/data/chromium-${MONITOR}`,
|
||||||
'--ozone-platform=wayland',
|
`--window-position=${geometry.x},${geometry.y}`,
|
||||||
`--user-data-dir=/data/chromium-${WORKSPACE}`,
|
`--window-size=${geometry.width},${geometry.height}`
|
||||||
'--window-position=0,0'
|
|
||||||
];
|
];
|
||||||
|
|
||||||
if (isKiosk) {
|
if (isKiosk) {
|
||||||
flags.push('--kiosk');
|
flags.push('--kiosk');
|
||||||
|
// To ensure full screen stays on the target monitor
|
||||||
|
flags.push(`--app=${currentUrl}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isGpu) {
|
if (!isGpu) {
|
||||||
@@ -57,24 +86,20 @@ async function startChromium() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const flags = buildFlags();
|
const flags = buildFlags();
|
||||||
// Start with a specific title so we can target it with swaymsg
|
|
||||||
const initialUrl = `data:text/html,<html><head><title>KioskInit${WORKSPACE}</title></head><body>Loading...</body></html>`;
|
|
||||||
|
|
||||||
console.log('Starting Chromium with flags:', flags.join(' '));
|
console.log('Starting Chromium with flags:', flags.join(' '));
|
||||||
|
|
||||||
browserProcess = spawn('/usr/bin/chromium', [...flags, initialUrl], {
|
// If kiosk is enabled, we pass the URL via --app to enforce window boundaries
|
||||||
|
// Otherwise we just pass the URL as a positional argument.
|
||||||
|
const args = [...flags];
|
||||||
|
if (!isKiosk) {
|
||||||
|
args.push(currentUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
browserProcess = spawn('/usr/bin/chromium', args, {
|
||||||
stdio: 'inherit'
|
stdio: 'inherit'
|
||||||
});
|
});
|
||||||
|
|
||||||
// Wait a bit for the window to appear in Sway
|
|
||||||
setTimeout(() => {
|
|
||||||
console.log(`Assigning window to workspace ${WORKSPACE}...`);
|
|
||||||
exec(`swaymsg "[title=KioskInit${WORKSPACE}] move to workspace ${WORKSPACE}"`, (err, stdout, stderr) => {
|
|
||||||
if (err) console.error('Swaymsg error:', stderr);
|
|
||||||
else console.log('Swaymsg success:', stdout);
|
|
||||||
});
|
|
||||||
}, 2000);
|
|
||||||
|
|
||||||
// Connect Puppeteer
|
// Connect Puppeteer
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -87,8 +112,10 @@ async function startChromium() {
|
|||||||
const pages = await browser.pages();
|
const pages = await browser.pages();
|
||||||
page = pages.length > 0 ? pages[0] : await browser.newPage();
|
page = pages.length > 0 ? pages[0] : await browser.newPage();
|
||||||
|
|
||||||
|
if (!isKiosk) {
|
||||||
console.log(`Navigating to ${currentUrl}`);
|
console.log(`Navigating to ${currentUrl}`);
|
||||||
await page.goto(currentUrl);
|
await page.goto(currentUrl);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Puppeteer connect error:', e);
|
console.error('Puppeteer connect error:', e);
|
||||||
}
|
}
|
||||||
@@ -128,7 +155,6 @@ app.post('/autorefresh/:interval', (req, res) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
app.post('/scan', (req, res) => {
|
app.post('/scan', (req, res) => {
|
||||||
// Stub for local service discovery
|
|
||||||
res.status(200).send('Scanned');
|
res.status(200).send('Scanned');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -157,7 +183,9 @@ app.post('/url', async (req, res) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (needRestart) {
|
// If we're in kiosk mode, --app flag is used so changing URL requires restart to stay full screen
|
||||||
|
// If we're not in kiosk mode, we can just navigate the page
|
||||||
|
if (needRestart || isKiosk) {
|
||||||
await startChromium();
|
await startChromium();
|
||||||
} else if (page) {
|
} else if (page) {
|
||||||
await page.goto(currentUrl);
|
await page.goto(currentUrl);
|
||||||
@@ -205,27 +233,16 @@ app.get('/version', (req, res) => {
|
|||||||
|
|
||||||
app.get('/screenshot', (req, res) => {
|
app.get('/screenshot', (req, res) => {
|
||||||
const file = `/tmp/screenshot-${Date.now()}.png`;
|
const file = `/tmp/screenshot-${Date.now()}.png`;
|
||||||
// Using grim instead of scrot for Wayland
|
// We use scrot for X11 screenshots.
|
||||||
// We specify the output by workspace name if needed, but grim captures the whole screen if no output is specified.
|
exec(`scrot ${file}`, (err) => {
|
||||||
// To capture just the workspace, we can use grim with swaymsg to find the coordinates,
|
if (err) return res.status(500).send('Screenshot failed');
|
||||||
// or simply rely on grim which by default captures everything.
|
|
||||||
// To be safe and capture just the output for this workspace:
|
|
||||||
exec(`grim -o HDMI-A-${WORKSPACE} ${file}`, (err) => {
|
|
||||||
if (err) {
|
|
||||||
// Fallback if HDMI-A-X is not found (e.g. testing locally)
|
|
||||||
exec(`grim ${file}`, (err2) => {
|
|
||||||
if (err2) return res.status(500).send('Screenshot failed');
|
|
||||||
res.sendFile(file);
|
res.sendFile(file);
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
res.sendFile(file);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Start initially
|
// Start initially
|
||||||
startChromium();
|
startChromium();
|
||||||
|
|
||||||
app.listen(PORT, () => {
|
app.listen(PORT, () => {
|
||||||
console.log(`Kiosk API running on port ${PORT}`);
|
console.log(`Kiosk API running on port ${PORT} for monitor ${MONITOR}`);
|
||||||
});
|
});
|
||||||
|
|||||||
+7
-4
@@ -1,12 +1,15 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
# Wait for Wayland socket to be available
|
# Wait for X11 socket to be available
|
||||||
echo "Waiting for Wayland display $WAYLAND_DISPLAY..."
|
echo "Waiting for X11 display $DISPLAY..."
|
||||||
while [ ! -e "$XDG_RUNTIME_DIR/$WAYLAND_DISPLAY" ]; do
|
while [ ! -S /tmp/.X11-unix/X0 ]; do
|
||||||
sleep 1
|
sleep 1
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "Wayland display found. Starting API server..."
|
echo "X11 display found. Starting API server..."
|
||||||
|
|
||||||
|
# Disable screen blanking for this display as well just in case
|
||||||
|
xset s off -dpms || true
|
||||||
|
|
||||||
# Start the Node.js API server
|
# Start the Node.js API server
|
||||||
exec node server.js
|
exec node server.js
|
||||||
|
|||||||
Reference in New Issue
Block a user