Web Kiosk 模式完整指南
什麼是 Kiosk 模式?
💡 比喻:把瀏覽器變成專用電視 你家的電視只能看電視節目,不能拿來上網、玩遊戲。 Kiosk 模式就是把一台電腦「鎖」成只能顯示一個網頁, 就像把一台電腦變成一台專用電視——只顯示你要的畫面,用戶無法跳出。
常見 Kiosk 應用場景
Kiosk 模式常見場景:
🏪 POS 收銀系統 → 只顯示收銀介面
🏥 醫院叫號系統 → 只顯示叫號畫面
🍔 速食店點餐機 → 只顯示點餐頁面
🏨 飯店 Check-in → 只顯示自助入住
🚉 車站資訊看板 → 只顯示時刻表
📊 工廠看板系統 → 只顯示即時產量
Chromium Kiosk Mode 基本設定
安裝 Chromium
# 更新套件庫 // 確保取得最新版本
sudo apt update // 執行套件更新
# 安裝 Chromium 瀏覽器 // Kiosk 模式的核心
sudo apt install -y chromium-browser // 安裝 Chromium
# 安裝無頭顯示套件 // 虛擬螢幕支援
sudo apt install -y xdotool unclutter // 安裝輔助工具
# 確認安裝成功 // 檢查版本號
chromium-browser --version // 顯示 Chromium 版本
基本 Kiosk 啟動指令
# 啟動全螢幕 Kiosk 模式 // 最基本的 Kiosk 指令
chromium-browser \
--kiosk \ // 啟用 Kiosk 模式
--noerrdialogs \ // 不顯示錯誤對話框
--disable-infobars \ // 不顯示資訊列
--no-first-run \ // 跳過首次執行設定
--disable-translate \ // 停用翻譯提示
--disable-features=TranslateUI \ // 停用翻譯 UI
--check-for-update-interval=31536000 \ // 停用自動更新檢查
--incognito \ // 無痕模式(不留紀錄)
'http://localhost:5000' // 指定要顯示的網頁
自動開機啟動 Kiosk
方法一:使用 autostart 設定
# 建立 autostart 目錄 // 存放自動啟動設定
mkdir -p /home/pi/.config/autostart // 建立目錄
# 建立 autostart 桌面檔案 // 自動啟動設定
cat > /home/pi/.config/autostart/kiosk.desktop << 'EOF' // 寫入設定檔
[Desktop Entry] // 桌面項目設定區塊
Type=Application // 類型為應用程式
Name=Kiosk // 名稱為 Kiosk
Exec=/home/pi/kiosk.sh // 執行啟動腳本
X-GNOME-Autostart-enabled=true // 啟用自動啟動
EOF
方法二:使用 systemd service
# 建立 systemd service 檔案 // 系統服務設定
sudo cat > /etc/systemd/system/kiosk.service << 'EOF' // 寫入 service 檔
[Unit] // 單元設定區塊
Description=Chromium Kiosk Mode // 服務描述
Wants=graphical.target // 需要圖形介面
After=graphical.target // 在圖形介面後啟動
[Service] // 服務設定區塊
Environment=DISPLAY=:0 // 設定顯示器環境變數
Environment=XAUTHORITY=/home/pi/.Xauthority // 設定 X 授權
Type=simple // 服務類型為簡單
ExecStart=/home/pi/kiosk.sh // 啟動腳本路徑
Restart=on-failure // 失敗時自動重啟
RestartSec=5 // 重啟間隔 5 秒
User=pi // 以 pi 用戶執行
[Install] // 安裝設定區塊
WantedBy=graphical.target // 隨圖形介面啟動
EOF
# 啟用並啟動服務 // 讓服務開機自動執行
sudo systemctl enable kiosk.service // 啟用開機自動啟動
sudo systemctl start kiosk.service // 立即啟動服務
sudo systemctl status kiosk.service // 檢查服務狀態
防止用戶跳出(禁用快捷鍵)
禁用常見快捷鍵
# 建立禁用快捷鍵腳本 // 防止用戶跳出 Kiosk
cat > /home/pi/disable_shortcuts.sh << 'EOF' // 寫入腳本
#!/bin/bash // 指定 Bash 直譯器
# 禁用 Alt+F4 // 防止關閉視窗
xdotool key --clearmodifiers alt+F4 // 清除 Alt+F4 綁定
# 禁用 Ctrl+Alt+Delete // 防止開啟工作管理員
xdg-settings set default-web-browser chromium-browser.desktop // 固定瀏覽器
# 使用 xbindkeys 攔截快捷鍵 // 進階快捷鍵管理
cat > /home/pi/.xbindkeysrc << 'KEYS' // 寫入快捷鍵設定
"echo '已禁用'" // 快捷鍵動作(無效化)
Alt + F4 // 攔截 Alt+F4
"echo '已禁用'" // 快捷鍵動作(無效化)
Control + w // 攔截 Ctrl+W
"echo '已禁用'" // 快捷鍵動作(無效化)
Alt + Tab // 攔截 Alt+Tab
KEYS
# 啟動 xbindkeys // 套用快捷鍵攔截
xbindkeys // 執行快捷鍵攔截
EOF
chmod +x /home/pi/disable_shortcuts.sh // 設定腳本可執行
隱藏滑鼠游標
# 安裝 unclutter // 自動隱藏閒置游標
sudo apt install -y unclutter // 安裝 unclutter
# 啟動時隱藏游標 // 5 秒無操作就隱藏
unclutter -idle 5 -root & // 背景執行隱藏游標
觸控螢幕校準
安裝校準工具
# 安裝觸控校準工具 // 讓觸控點對準螢幕位置
sudo apt install -y xinput-calibrator // 安裝 xinput-calibrator
# 執行校準程序 // 跟著螢幕點四個角落
xinput_calibrator // 啟動校準精靈
# 查看觸控裝置列表 // 找出觸控螢幕裝置 ID
xinput list // 列出所有輸入裝置
使用 C# 處理觸控事件
// 觸控螢幕事件處理服務 // 處理 Kiosk 的觸控輸入
public class TouchScreenService // 觸控螢幕服務類別
{
private readonly ILogger<TouchScreenService> _logger; // 日誌記錄器
// 建構函式注入日誌服務 // 依賴注入模式
public TouchScreenService(ILogger<TouchScreenService> logger) // 注入 Logger
{
_logger = logger; // 儲存日誌記錄器
}
// 處理觸控座標 // 將觸控位置轉換為畫面座標
public (int X, int Y) CalibrateTouch( // 校準觸控座標方法
int rawX, int rawY, // 原始觸控座標
int screenWidth, int screenHeight) // 螢幕解析度
{
// 校準比例計算 // 將原始座標映射到螢幕
double scaleX = (double)screenWidth / 4096; // X 軸縮放比例
double scaleY = (double)screenHeight / 4096; // Y 軸縮放比例
int calibratedX = (int)(rawX * scaleX); // 計算校準後 X
int calibratedY = (int)(rawY * scaleY); // 計算校準後 Y
_logger.LogDebug("觸控校準:({RawX},{RawY}) → ({CalX},{CalY})", // 記錄校準結果
rawX, rawY, calibratedX, calibratedY); // 傳入參數
return (calibratedX, calibratedY); // 回傳校準後座標
}
}
螢幕旋轉設定(直式/橫式)
旋轉螢幕
# 查看目前螢幕資訊 // 確認螢幕名稱
xrandr // 顯示螢幕資訊
# 旋轉螢幕 90 度(直式) // 適合點餐機、叫號機
xrandr --output HDMI-1 --rotate left // 逆時針旋轉 90 度
# 旋轉螢幕 180 度(倒掛) // 適合天花板螢幕
xrandr --output HDMI-1 --rotate inverted // 旋轉 180 度
# 恢復橫式 // 標準方向
xrandr --output HDMI-1 --rotate normal // 恢復正常方向
# 永久設定旋轉 // 寫入啟動設定
echo 'xrandr --output HDMI-1 --rotate left' >> /home/pi/.bashrc // 開機自動旋轉
旋轉觸控座標同步
# 旋轉觸控座標對應 // 螢幕旋轉後觸控也要跟著轉
TOUCH_ID=$(xinput list --id-only "touchscreen") // 取得觸控裝置 ID
# 設定觸控旋轉矩陣(左轉 90 度) // 數學矩陣轉換
xinput set-prop $TOUCH_ID 'Coordinate Transformation Matrix' \
0 -1 1 \ // 旋轉矩陣第一列
1 0 0 \ // 旋轉矩陣第二列
0 0 1 // 旋轉矩陣第三列
自動重新整理(避免記憶體洩漏)
JavaScript 自動重新整理
// Kiosk 頁面自動重新整理服務 // 防止長時間執行記憶體洩漏
public class KioskRefreshService // 自動重新整理服務類別
{
// 產生自動重新整理的 JavaScript // 注入到 Kiosk 頁面
public string GenerateAutoRefreshScript( // 產生 JS 腳本方法
int intervalMinutes = 30) // 預設每 30 分鐘刷新
{
// 回傳 JavaScript 程式碼 // 定時重新整理頁面
return $@"
<script>
// 設定自動重新整理計時器 // 避免記憶體洩漏
setInterval(function() {{ // 每隔指定時間執行
// 檢查是否閒置 // 有人操作就不刷新
if (Date.now() - lastInteraction > {intervalMinutes * 60 * 1000}) {{ // 判斷閒置時間
location.reload(); // 重新整理頁面
}}
}}, {intervalMinutes * 60 * 1000}); // 設定間隔毫秒數
// 記錄最後互動時間 // 追蹤用戶操作
var lastInteraction = Date.now(); // 初始化互動時間
document.addEventListener('click', function() {{ // 監聽點擊事件
lastInteraction = Date.now(); // 更新互動時間
}});
document.addEventListener('touchstart', function() {{ // 監聽觸控事件
lastInteraction = Date.now(); // 更新互動時間
}});
</script>"; // 結束 JavaScript 區塊
}
}
記憶體監控自動重啟
# 記憶體監控腳本 // 記憶體不足時自動重啟 Chromium
cat > /home/pi/monitor_memory.sh << 'EOF' // 建立監控腳本
#!/bin/bash // 指定直譯器
while true; do // 無限迴圈監控
# 取得可用記憶體(MB) // 計算剩餘記憶體
FREE_MEM=$(free -m | awk '/^Mem:/{print $7}') // 取得可用 MB
# 如果可用記憶體低於 100MB // 觸發重啟條件
if [ "$FREE_MEM" -lt 100 ]; then // 判斷記憶體門檻
echo "$(date): 記憶體不足 ${FREE_MEM}MB,重啟 Chromium" // 記錄日誌
pkill -f chromium // 殺掉 Chromium 程序
sleep 3 // 等待 3 秒
/home/pi/kiosk.sh & // 重新啟動 Kiosk
fi
sleep 60 // 每 60 秒檢查一次
done
EOF
chmod +x /home/pi/monitor_memory.sh // 設定可執行權限
多螢幕設定(主螢幕+客顯)
雙螢幕配置
# 查看所有螢幕 // 確認有幾個輸出
xrandr --listmonitors // 列出所有螢幕
# 設定雙螢幕(延伸模式) // 主螢幕右邊接客顯
xrandr --output HDMI-1 --auto --primary \ // 主螢幕設定
--output HDMI-2 --auto --right-of HDMI-1 // 客顯在右邊
# 設定雙螢幕(鏡像模式) // 兩個螢幕顯示一樣
xrandr --output HDMI-1 --auto --primary \ // 主螢幕設定
--output HDMI-2 --auto --same-as HDMI-1 // 客顯鏡像
C# 雙螢幕 Kiosk 管理
// 雙螢幕 Kiosk 管理器 // 管理主螢幕和客顯
public class DualScreenKiosk // 雙螢幕管理類別
{
// 螢幕配置模型 // 記錄每個螢幕的設定
public class ScreenConfig // 螢幕設定類別
{
public string OutputName { get; set; } = ""; // 螢幕輸出名稱(如 HDMI-1)
public int Width { get; set; } // 螢幕寬度像素
public int Height { get; set; } // 螢幕高度像素
public string Role { get; set; } = "main"; // 角色:main 或 customer
public string Url { get; set; } = ""; // 要顯示的網址
}
// 產生雙螢幕啟動腳本 // 自動開兩個 Chromium 視窗
public string GenerateLaunchScript( // 產生啟動腳本方法
ScreenConfig mainScreen, // 主螢幕設定
ScreenConfig customerScreen) // 客顯設定
{
// 組合 bash 啟動腳本 // 分別在兩個螢幕開 Chromium
return $@"#!/bin/bash
# 主螢幕 Kiosk // 操作員使用的畫面
chromium-browser --kiosk --window-position=0,0 \
--window-size={mainScreen.Width},{mainScreen.Height} \
'{mainScreen.Url}' &
# 客顯螢幕 // 給客人看的畫面
chromium-browser --kiosk --window-position={mainScreen.Width},0 \
--window-size={customerScreen.Width},{customerScreen.Height} \
'{customerScreen.Url}' &
"; // 結束腳本字串
}
}
完整的 kiosk.sh 啟動腳本
#!/bin/bash // 指定 Bash 直譯器
# kiosk.sh - 完整的 Kiosk 啟動腳本 // 一鍵啟動所有 Kiosk 元件
# === 基本設定 === // 可依需求修改
KIOSK_URL="http://localhost:5000" // Kiosk 要顯示的網址
SCREEN_ROTATION="normal" // 螢幕旋轉:normal/left/right/inverted
REFRESH_INTERVAL=1800 // 自動重新整理間隔(秒)
LOG_FILE="/home/pi/kiosk.log" // 日誌檔案路徑
# === 記錄啟動時間 === // 方便除錯
echo "$(date): Kiosk 啟動中..." >> $LOG_FILE // 寫入啟動日誌
# === 等待桌面環境就緒 === // 確保 X11 已啟動
sleep 5 // 等待 5 秒讓桌面就緒
# === 螢幕旋轉 === // 依設定旋轉螢幕
xrandr --output HDMI-1 --rotate $SCREEN_ROTATION // 執行螢幕旋轉
# === 隱藏游標 === // 無操作 3 秒後隱藏
unclutter -idle 3 -root & // 背景隱藏游標
# === 停用螢幕保護 === // 防止螢幕變黑
xset s off // 關閉螢幕保護
xset -dpms // 關閉電源管理
xset s noblank // 關閉螢幕空白
# === 禁用快捷鍵 === // 防止用戶跳出
xbindkeys // 啟動快捷鍵攔截
# === 清除 Chromium 快取 === // 避免快取問題
rm -rf /home/pi/.config/chromium/Default/Cache/* // 清除快取檔案
# === 啟動 Chromium Kiosk === // 主要 Kiosk 程序
chromium-browser \
--kiosk \ // 全螢幕 Kiosk 模式
--noerrdialogs \ // 隱藏錯誤對話框
--disable-infobars \ // 隱藏資訊列
--no-first-run \ // 跳過首次執行
--disable-translate \ // 停用翻譯
--disable-features=TranslateUI \ // 停用翻譯介面
--disable-session-crashed-bubble \ // 停用當機氣泡
--disable-component-update \ // 停用元件更新
--check-for-update-interval=31536000 \ // 極長更新間隔
--incognito \ // 無痕模式
--disk-cache-size=0 \ // 不使用磁碟快取
"$KIOSK_URL" & // 開啟目標網址
# === 記錄啟動完成 === // 確認 Kiosk 已啟動
echo "$(date): Kiosk 啟動完成" >> $LOG_FILE // 寫入完成日誌
錯誤自動重啟腳本
#!/bin/bash // 指定直譯器
# watchdog.sh - 監控並自動重啟 // Kiosk 的看門狗
KIOSK_URL="http://localhost:5000" // Kiosk 網址
CHECK_INTERVAL=30 // 每 30 秒檢查一次
LOG_FILE="/home/pi/watchdog.log" // 看門狗日誌
while true; do // 無限迴圈監控
# 檢查 Chromium 是否在執行 // 確認程序存活
if ! pgrep -x "chromium" > /dev/null; then // 如果 Chromium 沒在跑
echo "$(date): Chromium 已停止,重新啟動..." >> $LOG_FILE // 記錄異常
/home/pi/kiosk.sh & // 重新啟動 Kiosk
fi
# 檢查網頁是否能連線 // 確認 Web Server 正常
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" $KIOSK_URL) // 取得 HTTP 狀態碼
if [ "$HTTP_CODE" != "200" ]; then // 如果不是 200 OK
echo "$(date): 網頁回應 $HTTP_CODE,等待恢復..." >> $LOG_FILE // 記錄異常
fi
sleep $CHECK_INTERVAL // 等待下次檢查
done
🤔 我這樣寫為什麼會錯?
❌ 錯誤:直接執行 chromium --kiosk 卻看到白畫面
# 錯誤寫法 // 沒等 Web Server 就啟動 Kiosk
chromium-browser --kiosk http://localhost:5000 // 直接啟動(Web Server 還沒準備好)
✅ 正確:先等 Web Server 就緒再啟動
# 正確寫法 // 確認 Web Server 已啟動
while ! curl -s http://localhost:5000 > /dev/null; do // 迴圈等待 Server 就緒
echo "等待 Web Server 啟動..." // 顯示等待訊息
sleep 2 // 每 2 秒重試
done
chromium-browser --kiosk http://localhost:5000 // Server 就緒後再啟動 Kiosk
❌ 錯誤:螢幕旋轉後觸控位置偏移
# 錯誤寫法 // 只旋轉螢幕沒旋轉觸控
xrandr --output HDMI-1 --rotate left // 旋轉螢幕
# 觸控座標還是原本的方向 ← 點哪裡都不對! // 忘記同步觸控矩陣
✅ 正確:螢幕和觸控一起旋轉
# 正確寫法 // 螢幕和觸控都要旋轉
xrandr --output HDMI-1 --rotate left // 旋轉螢幕
xinput set-prop 'touchscreen' 'Coordinate Transformation Matrix' \ // 同步旋轉觸控
0 -1 1 1 0 0 0 0 1 // 左轉 90 度的矩陣
❌ 錯誤:Kiosk 記憶體越用越多
// 錯誤寫法 // 沒有自動重新整理機制
public class KioskPage // Kiosk 頁面類別
{
// 頁面永遠不刷新 // 長時間運行記憶體會洩漏
public void Render() // 渲染頁面方法
{
// 只渲染一次就不管了 // 記憶體只增不減
}
}
✅ 正確:加入定時重新整理和記憶體監控
// 正確寫法 // 定期重新整理 + 監控記憶體
public class SmartKioskPage // 智慧 Kiosk 頁面類別
{
private readonly Timer _refreshTimer; // 重新整理計時器
public SmartKioskPage() // 建構函式
{
// 每 30 分鐘自動重新整理 // 避免記憶體洩漏
_refreshTimer = new Timer(RefreshPage, null, // 建立計時器
TimeSpan.FromMinutes(30), // 30 分鐘後第一次觸發
TimeSpan.FromMinutes(30)); // 之後每 30 分鐘觸發
}
private void RefreshPage(object? state) // 重新整理回呼方法
{
// 檢查是否有用戶正在操作 // 避免打斷操作
if (IsUserIdle()) // 如果用戶閒置
{
ForceRefresh(); // 執行頁面重新整理
}
}
private bool IsUserIdle() => true; // 判斷用戶是否閒置
private void ForceRefresh() { } // 強制重新整理
}