update at 2026-03-16 09:00:35
This commit is contained in:
107
dash/src/dash.sh
107
dash/src/dash.sh
@@ -16,12 +16,21 @@ SLEEP_SCREEN_INTERVAL=${SLEEP_SCREEN_INTERVAL:-3600}
|
||||
DISABLE_SYSTEM_SUSPEND=${DISABLE_SYSTEM_SUSPEND:-false}
|
||||
BACKGROUND_REFRESH_INTERVAL_MINUTES=${BACKGROUND_REFRESH_INTERVAL_MINUTES:-120}
|
||||
CLOCK_FULL_REFRESH_INTERVAL_MINUTES=${CLOCK_FULL_REFRESH_INTERVAL_MINUTES:-15}
|
||||
PRE_SLEEP_GRACE_SECONDS=${PRE_SLEEP_GRACE_SECONDS:-10}
|
||||
STATUS_MASK_ENABLED=${STATUS_MASK_ENABLED:-true}
|
||||
STATUS_MASK_LEFT=${STATUS_MASK_LEFT:-700}
|
||||
STATUS_MASK_TOP=${STATUS_MASK_TOP:-0}
|
||||
STATUS_MASK_WIDTH=${STATUS_MASK_WIDTH:-372}
|
||||
STATUS_MASK_HEIGHT=${STATUS_MASK_HEIGHT:-24}
|
||||
STATUS_MASK_PASSES=${STATUS_MASK_PASSES:-3}
|
||||
STATUS_MASK_DELAY_SECONDS=${STATUS_MASK_DELAY_SECONDS:-1}
|
||||
RTC=/sys/devices/platform/mxc_rtc.0/wakeup_enable
|
||||
|
||||
LOW_BATTERY_REPORTING=${LOW_BATTERY_REPORTING:-false}
|
||||
LOW_BATTERY_THRESHOLD_PERCENT=${LOW_BATTERY_THRESHOLD_PERCENT:-10}
|
||||
|
||||
num_refresh=0
|
||||
background_needs_redraw=true
|
||||
|
||||
init() {
|
||||
if [ -z "$TIMEZONE" ] || [ -z "$REFRESH_SCHEDULE" ]; then
|
||||
@@ -38,16 +47,28 @@ init() {
|
||||
echo "System suspend disabled, using normal sleep between refreshes."
|
||||
fi
|
||||
|
||||
/etc/init.d/framework stop
|
||||
stop_framework
|
||||
initctl stop webreader >/dev/null 2>&1
|
||||
echo powersave >/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
|
||||
lipc-set-prop com.lab126.powerd preventScreenSaver 1
|
||||
}
|
||||
|
||||
stop_framework() {
|
||||
# 不同 Kindle 固件停止 framework 的入口不完全一致。
|
||||
# Voyage 5.13.6 上没有 /etc/init.d/framework,需要走 upstart 入口。
|
||||
if [ -x /etc/init.d/framework ]; then
|
||||
/etc/init.d/framework stop || true
|
||||
return
|
||||
fi
|
||||
|
||||
stop framework >/dev/null 2>&1 || initctl stop framework >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
prepare_sleep() {
|
||||
echo "Preparing sleep"
|
||||
|
||||
/usr/sbin/eips -f -g "$DIR/sleeping.png"
|
||||
background_needs_redraw=true
|
||||
|
||||
# Give screen time to refresh
|
||||
sleep 2
|
||||
@@ -97,6 +118,27 @@ clock_force_full_refresh() {
|
||||
[ $((minute % CLOCK_FULL_REFRESH_INTERVAL_MINUTES)) -eq 0 ]
|
||||
}
|
||||
|
||||
mask_system_status_overlay() {
|
||||
if [ "$STATUS_MASK_ENABLED" != true ]; then
|
||||
return
|
||||
fi
|
||||
|
||||
# Voyage 上 framework/appmgrd 偶尔会把右上角时间与状态图标重新画回屏幕。
|
||||
# 这里在每次 dashboard 刷新后,用顶部空白带的白底把它盖掉。
|
||||
# 实测需要延迟后再补盖一次,否则系统可能会在我们第一次覆盖后再重画一遍。
|
||||
pass=1
|
||||
while [ "$pass" -le "$STATUS_MASK_PASSES" ]; do
|
||||
fbink -q -V -B WHITE -k \
|
||||
"top=$STATUS_MASK_TOP,left=$STATUS_MASK_LEFT,width=$STATUS_MASK_WIDTH,height=$STATUS_MASK_HEIGHT"
|
||||
|
||||
if [ "$pass" -lt "$STATUS_MASK_PASSES" ] && [ "$STATUS_MASK_DELAY_SECONDS" -gt 0 ]; then
|
||||
sleep "$STATUS_MASK_DELAY_SECONDS"
|
||||
fi
|
||||
|
||||
pass=$((pass + 1))
|
||||
done
|
||||
}
|
||||
|
||||
refresh_dashboard() {
|
||||
background_refreshed=false
|
||||
|
||||
@@ -116,6 +158,16 @@ refresh_dashboard() {
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$background_refreshed" = false ] && [ "$background_needs_redraw" = true ]; then
|
||||
echo "Restoring cached background"
|
||||
/usr/sbin/eips -f -g "$BACKGROUND_PNG"
|
||||
background_needs_redraw=false
|
||||
fi
|
||||
|
||||
if [ "$background_refreshed" = true ]; then
|
||||
background_needs_redraw=false
|
||||
fi
|
||||
|
||||
if [ "$background_refreshed" = true ] || clock_force_full_refresh; then
|
||||
echo "Clock patch full refresh"
|
||||
"$CLOCK_RENDER_CMD" true
|
||||
@@ -124,18 +176,40 @@ refresh_dashboard() {
|
||||
"$CLOCK_RENDER_CMD" false
|
||||
fi
|
||||
|
||||
mask_system_status_overlay
|
||||
|
||||
num_refresh=$((num_refresh + 1))
|
||||
}
|
||||
|
||||
powerd_get_prop() {
|
||||
prop_name=$1
|
||||
|
||||
if ! command -v lipc-get-prop >/dev/null 2>&1; then
|
||||
echo "unavailable"
|
||||
return 0
|
||||
fi
|
||||
|
||||
lipc-get-prop com.lab126.powerd "$prop_name" 2>/dev/null || echo "unavailable"
|
||||
}
|
||||
|
||||
log_battery_stats() {
|
||||
battery_level=$(gasgauge-info -c)
|
||||
echo "$(date) Battery level: $battery_level."
|
||||
battery_level=$(gasgauge-info -c 2>/dev/null || echo "unknown")
|
||||
charging_state=$(powerd_get_prop isCharging)
|
||||
battery_state_info=$(powerd_get_prop battStateInfo)
|
||||
|
||||
# 同时记录 powerd 的充电标志与原始电池状态,便于直接从 dash.log
|
||||
# 判断 Kindle 是否识别到外部供电,以及是否真的在充电。
|
||||
echo "$(date) Battery level: $battery_level. isCharging: $charging_state. battStateInfo: $battery_state_info."
|
||||
|
||||
if [ "$LOW_BATTERY_REPORTING" = true ]; then
|
||||
battery_level_numeric=${battery_level%?}
|
||||
if [ "$battery_level_numeric" -le "$LOW_BATTERY_THRESHOLD_PERCENT" ]; then
|
||||
"$LOW_BATTERY_CMD" "$battery_level_numeric"
|
||||
fi
|
||||
case "$battery_level" in
|
||||
*%)
|
||||
battery_level_numeric=${battery_level%?}
|
||||
if [ "$battery_level_numeric" -le "$LOW_BATTERY_THRESHOLD_PERCENT" ]; then
|
||||
"$LOW_BATTERY_CMD" "$battery_level_numeric"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -160,20 +234,31 @@ main_loop() {
|
||||
|
||||
next_wakeup_secs=$("$DIR/next-wakeup" --schedule="$REFRESH_SCHEDULE" --timezone="$TIMEZONE")
|
||||
|
||||
if [ "$next_wakeup_secs" -gt "$SLEEP_SCREEN_INTERVAL" ]; then
|
||||
if [ "$next_wakeup_secs" -gt "$SLEEP_SCREEN_INTERVAL" ] && [ "$DISABLE_SYSTEM_SUSPEND" != true ]; then
|
||||
action="sleep"
|
||||
prepare_sleep
|
||||
else
|
||||
if [ "$next_wakeup_secs" -gt "$SLEEP_SCREEN_INTERVAL" ] && [ "$DISABLE_SYSTEM_SUSPEND" = true ]; then
|
||||
echo "Debug mode active, skipping sleeping screen."
|
||||
fi
|
||||
action="suspend"
|
||||
refresh_dashboard
|
||||
fi
|
||||
|
||||
# take a bit of time before going to sleep, so this process can be aborted
|
||||
sleep 10
|
||||
actual_sleep_secs=$next_wakeup_secs
|
||||
if [ "$actual_sleep_secs" -gt "$PRE_SLEEP_GRACE_SECONDS" ]; then
|
||||
actual_sleep_secs=$((actual_sleep_secs - PRE_SLEEP_GRACE_SECONDS))
|
||||
else
|
||||
actual_sleep_secs=0
|
||||
fi
|
||||
|
||||
# 预留一小段可中断窗口,便于在 Kindle 本机或 SSH 下手动终止进程。
|
||||
# 这段时间必须从 rtc_sleep 中扣掉,否则每分钟刷新会长期晚于计划时间。
|
||||
sleep "$PRE_SLEEP_GRACE_SECONDS"
|
||||
|
||||
echo "Going to $action, next wakeup in ${next_wakeup_secs}s"
|
||||
|
||||
rtc_sleep "$next_wakeup_secs"
|
||||
rtc_sleep "$actual_sleep_secs"
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,9 @@ END {
|
||||
mv "$TMP_FILE" "$ENV_FILE"
|
||||
|
||||
# 已运行的 dashboard 进程不会重新读取 env.sh,切换后先停掉它,
|
||||
# 避免旧进程继续按旧配置运行。
|
||||
# 然后立刻拉起新的 dashboard,避免用户还要再次手动启动。
|
||||
pkill -f "$DIR/dash.sh" 2>/dev/null || true
|
||||
sleep 1
|
||||
"$DIR/start.sh"
|
||||
|
||||
echo "已关闭 Dashboard 调试模式。当前 Dashboard 已停止,请重新启动 Kindle Dashboard。"
|
||||
echo "已关闭 Dashboard 调试模式,并自动重启 Kindle Dashboard。"
|
||||
|
||||
@@ -35,7 +35,9 @@ END {
|
||||
mv "$TMP_FILE" "$ENV_FILE"
|
||||
|
||||
# 已运行的 dashboard 进程不会重新读取 env.sh,切换后先停掉它,
|
||||
# 避免旧进程继续按旧配置进入系统挂起。
|
||||
# 然后立刻拉起新的 dashboard,避免用户还要在短时间内再点一次菜单。
|
||||
pkill -f "$DIR/dash.sh" 2>/dev/null || true
|
||||
sleep 1
|
||||
"$DIR/start.sh"
|
||||
|
||||
echo "已开启 Dashboard 调试模式。当前 Dashboard 已停止,请重新启动 Kindle Dashboard。"
|
||||
echo "已开启 Dashboard 调试模式,并自动重启 Kindle Dashboard。"
|
||||
|
||||
@@ -11,8 +11,24 @@ to_decimal() {
|
||||
}'
|
||||
}
|
||||
|
||||
hour_value=${1:-$(date '+%H')}
|
||||
minute_value=${2:-$(date '+%M')}
|
||||
current_clock_values() {
|
||||
# 时钟渲染要和 dashboard 配置的时区保持一致。
|
||||
# 否则即使 next-wakeup 按 TIMEZONE 唤醒,指针仍会按系统默认时区取值。
|
||||
if [ -n "${TIMEZONE:-}" ]; then
|
||||
TZ="$TIMEZONE" date '+%H %M'
|
||||
else
|
||||
date '+%H %M'
|
||||
fi
|
||||
}
|
||||
|
||||
if [ "$#" -ge 2 ]; then
|
||||
hour_value=$1
|
||||
minute_value=$2
|
||||
else
|
||||
set -- $(current_clock_values)
|
||||
hour_value=$1
|
||||
minute_value=$2
|
||||
fi
|
||||
|
||||
hour_decimal=$(to_decimal "$hour_value")
|
||||
minute_decimal=$(to_decimal "$minute_value")
|
||||
|
||||
@@ -13,6 +13,37 @@ export CLOCK_REGION_WIDTH=${CLOCK_REGION_WIDTH:-220}
|
||||
export CLOCK_REGION_HEIGHT=${CLOCK_REGION_HEIGHT:-220}
|
||||
export CLOCK_FULL_REFRESH_INTERVAL_MINUTES=${CLOCK_FULL_REFRESH_INTERVAL_MINUTES:-15}
|
||||
|
||||
# 本机时钟外观参数:
|
||||
# 页面改版导致时钟区域尺寸变化时,通常只需要改 CLOCK_REGION_*,
|
||||
# 这组比例参数会随新的宽高自动缩放。
|
||||
# 如果只是想微调指针长短、粗细或刻度长度,再改下面这些值即可,不用改 Lua 代码。
|
||||
export CLOCK_FACE_RADIUS_RATIO=${CLOCK_FACE_RADIUS_RATIO:-0.47}
|
||||
export CLOCK_FACE_STROKE=${CLOCK_FACE_STROKE:-3}
|
||||
export CLOCK_TICK_OUTER_INSET=${CLOCK_TICK_OUTER_INSET:-6}
|
||||
export CLOCK_MAJOR_TICK_LENGTH=${CLOCK_MAJOR_TICK_LENGTH:-14}
|
||||
export CLOCK_MINOR_TICK_LENGTH=${CLOCK_MINOR_TICK_LENGTH:-7}
|
||||
export CLOCK_MAJOR_TICK_THICKNESS=${CLOCK_MAJOR_TICK_THICKNESS:-4}
|
||||
export CLOCK_MINOR_TICK_THICKNESS=${CLOCK_MINOR_TICK_THICKNESS:-2}
|
||||
export CLOCK_HOUR_LENGTH_RATIO=${CLOCK_HOUR_LENGTH_RATIO:-0.48}
|
||||
export CLOCK_MINUTE_LENGTH_RATIO=${CLOCK_MINUTE_LENGTH_RATIO:-0.72}
|
||||
export CLOCK_HOUR_THICKNESS=${CLOCK_HOUR_THICKNESS:-9}
|
||||
export CLOCK_MINUTE_THICKNESS=${CLOCK_MINUTE_THICKNESS:-5}
|
||||
export CLOCK_CENTER_RADIUS=${CLOCK_CENTER_RADIUS:-7}
|
||||
|
||||
# 进入 rtc suspend 前预留的可中断窗口,方便在调试时及时停止进程。
|
||||
# 这段时间会从真正的休眠时长里扣掉,避免分钟刷新慢一拍。
|
||||
export PRE_SLEEP_GRACE_SECONDS=${PRE_SLEEP_GRACE_SECONDS:-10}
|
||||
|
||||
# Voyage 顶部状态栏遮罩:用于压住系统偶尔重画出来的时间、Wi-Fi、电池图标。
|
||||
# 当前坐标只覆盖页面顶部空白带,不会擦到天气卡上边框。
|
||||
export STATUS_MASK_ENABLED=${STATUS_MASK_ENABLED:-true}
|
||||
export STATUS_MASK_LEFT=${STATUS_MASK_LEFT:-700}
|
||||
export STATUS_MASK_TOP=${STATUS_MASK_TOP:-0}
|
||||
export STATUS_MASK_WIDTH=${STATUS_MASK_WIDTH:-372}
|
||||
export STATUS_MASK_HEIGHT=${STATUS_MASK_HEIGHT:-24}
|
||||
export STATUS_MASK_PASSES=${STATUS_MASK_PASSES:-3}
|
||||
export STATUS_MASK_DELAY_SECONDS=${STATUS_MASK_DELAY_SECONDS:-1}
|
||||
|
||||
# By default, partial screen updates are used to update the screen,
|
||||
# to prevent the screen from flashing. After a few partial updates,
|
||||
# the screen will start to look a bit distorted (due to e-ink ghosting).
|
||||
@@ -21,7 +52,7 @@ export CLOCK_FULL_REFRESH_INTERVAL_MINUTES=${CLOCK_FULL_REFRESH_INTERVAL_MINUTES
|
||||
export FULL_DISPLAY_REFRESH_RATE=${FULL_DISPLAY_REFRESH_RATE:-0}
|
||||
|
||||
# 调试开关:设为 true 后,主循环仍会按计划拉图和刷新屏幕,
|
||||
# 但不会把 Kindle 写入 /sys/power/state 进入系统挂起。
|
||||
# 不会进入 sleeping.png 分支,也不会把 Kindle 写入 /sys/power/state 进入系统挂起。
|
||||
# 适合通过 KUAL 或普通 start.sh 连续观察效果,调试结束后再改回 false。
|
||||
export DISABLE_SYSTEM_SUSPEND=${DISABLE_SYSTEM_SUSPEND:-false}
|
||||
|
||||
|
||||
157
dash/src/local/render-clock.lua
Normal file
157
dash/src/local/render-clock.lua
Normal file
@@ -0,0 +1,157 @@
|
||||
-- 在 Kindle 本机生成一张时钟区域位图,然后交给 fbink 刷到屏幕。
|
||||
-- 这里不再依赖透明 PNG 叠图,避免 eips 处理 alpha 时出现整块发黑的问题。
|
||||
|
||||
local output_path = assert(arg[1], "missing output path")
|
||||
local width = tonumber((assert(arg[2], "missing width")))
|
||||
local height = tonumber((assert(arg[3], "missing height")))
|
||||
local hour_value = tonumber((assert(arg[4], "missing hour")))
|
||||
local minute_value = tonumber((assert(arg[5], "missing minute")))
|
||||
|
||||
local function number_arg(index, fallback)
|
||||
local value = arg[index]
|
||||
if value == nil or value == "" then
|
||||
return fallback
|
||||
end
|
||||
|
||||
local numeric = tonumber(value)
|
||||
if numeric == nil then
|
||||
return fallback
|
||||
end
|
||||
|
||||
return numeric
|
||||
end
|
||||
|
||||
local WHITE = 255
|
||||
local BLACK = 0
|
||||
local cx = (width - 1) / 2
|
||||
local cy = (height - 1) / 2
|
||||
local face_radius = math.floor(math.min(width, height) * number_arg(6, 0.47))
|
||||
local face_stroke = number_arg(7, 3)
|
||||
local tick_outer_inset = number_arg(8, 6)
|
||||
local major_tick_length = number_arg(9, 14)
|
||||
local minor_tick_length = number_arg(10, 7)
|
||||
local major_tick_thickness = number_arg(11, 4)
|
||||
local minor_tick_thickness = number_arg(12, 2)
|
||||
local hour_length_ratio = number_arg(13, 0.48)
|
||||
local minute_length_ratio = number_arg(14, 0.72)
|
||||
local hour_thickness = number_arg(15, 9)
|
||||
local minute_thickness = number_arg(16, 5)
|
||||
local center_radius = number_arg(17, 7)
|
||||
|
||||
local pixels = {}
|
||||
for index = 1, width * height do
|
||||
pixels[index] = WHITE
|
||||
end
|
||||
|
||||
local function pixel_index(x, y)
|
||||
return y * width + x + 1
|
||||
end
|
||||
|
||||
local function set_pixel(x, y, value)
|
||||
if x < 0 or y < 0 or x >= width or y >= height then
|
||||
return
|
||||
end
|
||||
|
||||
pixels[pixel_index(x, y)] = value
|
||||
end
|
||||
|
||||
local function fill_disk(x, y, radius, value)
|
||||
local r2 = radius * radius
|
||||
local min_x = math.floor(x - radius)
|
||||
local max_x = math.ceil(x + radius)
|
||||
local min_y = math.floor(y - radius)
|
||||
local max_y = math.ceil(y + radius)
|
||||
|
||||
for py = min_y, max_y do
|
||||
for px = min_x, max_x do
|
||||
local dx = px - x
|
||||
local dy = py - y
|
||||
if dx * dx + dy * dy <= r2 then
|
||||
set_pixel(px, py, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function draw_segment(x1, y1, x2, y2, thickness, value)
|
||||
local dx = x2 - x1
|
||||
local dy = y2 - y1
|
||||
local steps = math.max(math.abs(dx), math.abs(dy))
|
||||
|
||||
if steps == 0 then
|
||||
fill_disk(x1, y1, math.max(1, thickness / 2), value)
|
||||
return
|
||||
end
|
||||
|
||||
local radius = math.max(1, thickness / 2)
|
||||
for step = 0, steps do
|
||||
local t = step / steps
|
||||
local x = x1 + dx * t
|
||||
local y = y1 + dy * t
|
||||
fill_disk(x, y, radius, value)
|
||||
end
|
||||
end
|
||||
|
||||
local function draw_circle(radius, thickness, value)
|
||||
local samples = 720
|
||||
for step = 0, samples - 1 do
|
||||
local angle = (step / samples) * math.pi * 2
|
||||
local x = cx + math.cos(angle) * radius
|
||||
local y = cy + math.sin(angle) * radius
|
||||
fill_disk(x, y, thickness / 2, value)
|
||||
end
|
||||
end
|
||||
|
||||
local function draw_ticks()
|
||||
for tick = 0, 59 do
|
||||
local is_major = tick % 5 == 0
|
||||
local angle = (tick / 60) * math.pi * 2 - math.pi / 2
|
||||
local outer = face_radius - tick_outer_inset
|
||||
local inner = outer - (is_major and major_tick_length or minor_tick_length)
|
||||
local x1 = cx + math.cos(angle) * inner
|
||||
local y1 = cy + math.sin(angle) * inner
|
||||
local x2 = cx + math.cos(angle) * outer
|
||||
local y2 = cy + math.sin(angle) * outer
|
||||
draw_segment(x1, y1, x2, y2, is_major and major_tick_thickness or minor_tick_thickness, BLACK)
|
||||
end
|
||||
end
|
||||
|
||||
local function hand_endpoint(angle_deg, length)
|
||||
local angle = math.rad(angle_deg - 90)
|
||||
return cx + math.cos(angle) * length, cy + math.sin(angle) * length
|
||||
end
|
||||
|
||||
local function draw_hands()
|
||||
local hour_angle = (hour_value % 12) * 30 + minute_value * 0.5
|
||||
local minute_angle = minute_value * 6
|
||||
|
||||
local hour_x, hour_y = hand_endpoint(hour_angle, face_radius * hour_length_ratio)
|
||||
local minute_x, minute_y = hand_endpoint(minute_angle, face_radius * minute_length_ratio)
|
||||
|
||||
draw_segment(cx, cy, hour_x, hour_y, hour_thickness, BLACK)
|
||||
draw_segment(cx, cy, minute_x, minute_y, minute_thickness, BLACK)
|
||||
fill_disk(cx, cy, center_radius, BLACK)
|
||||
end
|
||||
|
||||
draw_circle(face_radius, face_stroke, BLACK)
|
||||
draw_ticks()
|
||||
draw_hands()
|
||||
|
||||
local file = assert(io.open(output_path, "wb"))
|
||||
file:write(string.format("P5\n%d %d\n255\n", width, height))
|
||||
|
||||
local char_cache = {
|
||||
[WHITE] = string.char(WHITE),
|
||||
[BLACK] = string.char(BLACK),
|
||||
}
|
||||
|
||||
for y = 0, height - 1 do
|
||||
local row = {}
|
||||
for x = 0, width - 1 do
|
||||
local value = pixels[pixel_index(x, y)]
|
||||
row[#row + 1] = char_cache[value] or string.char(value)
|
||||
end
|
||||
file:write(table.concat(row))
|
||||
end
|
||||
|
||||
file:close()
|
||||
@@ -2,31 +2,60 @@
|
||||
set -eu
|
||||
|
||||
DIR="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
|
||||
clock_face_path=${CLOCK_FACE_PATH:-"$DIR/../assets/clock-face.png"}
|
||||
hour_assets_dir=${CLOCK_HOUR_ASSETS_DIR:-"$DIR/../assets/hour-hand"}
|
||||
minute_assets_dir=${CLOCK_MINUTE_ASSETS_DIR:-"$DIR/../assets/minute-hand"}
|
||||
clock_region_x=${CLOCK_REGION_X:-313}
|
||||
clock_region_y=${CLOCK_REGION_Y:-0}
|
||||
ENV_FILE="$DIR/env.sh"
|
||||
|
||||
# 单独执行本脚本时,也需要读取同一份坐标配置。
|
||||
# 否则会退回到脚本内默认值,导致手工调试与主循环绘制位置不一致。
|
||||
# shellcheck disable=SC1090
|
||||
[ -f "$ENV_FILE" ] && . "$ENV_FILE"
|
||||
|
||||
clock_region_x=${CLOCK_REGION_X:-262}
|
||||
clock_region_y=${CLOCK_REGION_Y:-55}
|
||||
clock_region_width=${CLOCK_REGION_WIDTH:-220}
|
||||
clock_region_height=${CLOCK_REGION_HEIGHT:-220}
|
||||
clock_face_radius_ratio=${CLOCK_FACE_RADIUS_RATIO:-0.47}
|
||||
clock_face_stroke=${CLOCK_FACE_STROKE:-3}
|
||||
clock_tick_outer_inset=${CLOCK_TICK_OUTER_INSET:-6}
|
||||
clock_major_tick_length=${CLOCK_MAJOR_TICK_LENGTH:-14}
|
||||
clock_minor_tick_length=${CLOCK_MINOR_TICK_LENGTH:-7}
|
||||
clock_major_tick_thickness=${CLOCK_MAJOR_TICK_THICKNESS:-4}
|
||||
clock_minor_tick_thickness=${CLOCK_MINOR_TICK_THICKNESS:-2}
|
||||
clock_hour_length_ratio=${CLOCK_HOUR_LENGTH_RATIO:-0.48}
|
||||
clock_minute_length_ratio=${CLOCK_MINUTE_LENGTH_RATIO:-0.72}
|
||||
clock_hour_thickness=${CLOCK_HOUR_THICKNESS:-9}
|
||||
clock_minute_thickness=${CLOCK_MINUTE_THICKNESS:-5}
|
||||
clock_center_radius=${CLOCK_CENTER_RADIUS:-7}
|
||||
force_full_refresh=${1:-false}
|
||||
output_path="$DIR/state/clock-render.pgm"
|
||||
|
||||
eval "$("$DIR/clock-index.sh")"
|
||||
|
||||
hour_patch="$hour_assets_dir/$hour_index.png"
|
||||
minute_patch="$minute_assets_dir/$minute_index.png"
|
||||
mkdir -p "$DIR/state"
|
||||
|
||||
if [ ! -f "$clock_face_path" ] || [ ! -f "$hour_patch" ] || [ ! -f "$minute_patch" ]; then
|
||||
echo "Clock assets missing."
|
||||
echo "Face: $clock_face_path"
|
||||
echo "Hour: $hour_patch"
|
||||
echo "Minute: $minute_patch"
|
||||
exit 1
|
||||
fi
|
||||
lua "$DIR/render-clock.lua" \
|
||||
"$output_path" \
|
||||
"$clock_region_width" \
|
||||
"$clock_region_height" \
|
||||
"$hour" \
|
||||
"$minute" \
|
||||
"$clock_face_radius_ratio" \
|
||||
"$clock_face_stroke" \
|
||||
"$clock_tick_outer_inset" \
|
||||
"$clock_major_tick_length" \
|
||||
"$clock_minor_tick_length" \
|
||||
"$clock_major_tick_thickness" \
|
||||
"$clock_minor_tick_thickness" \
|
||||
"$clock_hour_length_ratio" \
|
||||
"$clock_minute_length_ratio" \
|
||||
"$clock_hour_thickness" \
|
||||
"$clock_minute_thickness" \
|
||||
"$clock_center_radius"
|
||||
|
||||
if [ "$force_full_refresh" = true ]; then
|
||||
/usr/sbin/eips -f -g "$clock_face_path" -x "$clock_region_x" -y "$clock_region_y"
|
||||
# Kindle Voyage 当前这条链路里,fbink 默认会叠加 viewport 修正,
|
||||
# 导致图像在屏幕上出现双重偏移。这里强制关闭 viewport 修正,
|
||||
# 让坐标与网页导出的像素坐标保持一致。
|
||||
fbink -q -V -f -g "file=$output_path,x=$clock_region_x,y=$clock_region_y"
|
||||
else
|
||||
/usr/sbin/eips -g "$clock_face_path" -x "$clock_region_x" -y "$clock_region_y"
|
||||
fbink -q -V -g "file=$output_path,x=$clock_region_x,y=$clock_region_y"
|
||||
fi
|
||||
|
||||
/usr/sbin/eips -g "$hour_patch" -x "$clock_region_x" -y "$clock_region_y"
|
||||
/usr/sbin/eips -g "$minute_patch" -x "$clock_region_x" -y "$clock_region_y"
|
||||
|
||||
@@ -14,5 +14,7 @@ mkdir -p "$(dirname "$LOG_FILE")"
|
||||
if [ "$DEBUG" = true ]; then
|
||||
"$DIR/dash.sh"
|
||||
else
|
||||
"$DIR/dash.sh" >>"$LOG_FILE" 2>&1 &
|
||||
# 通过 SSH 或 KUAL 触发时,父 shell 很快就会退出。
|
||||
# 这里必须用 nohup 脱离会话,否则后台的 dash.sh 会跟着收到 HUP 退出。
|
||||
nohup "$DIR/dash.sh" >>"$LOG_FILE" 2>&1 </dev/null &
|
||||
fi
|
||||
|
||||
@@ -1,2 +1,56 @@
|
||||
#!/usr/bin/env sh
|
||||
pkill -f dash.sh
|
||||
set -eu
|
||||
|
||||
# 退出 dashboard 时,不能只“启动一下 framework”就结束。
|
||||
# Voyage 5.13.6 上白屏往往来自 framework / webreader / cvm 半恢复状态:
|
||||
# 进程看起来在,但前台 Java UI 实际没起来。
|
||||
# 这里统一做一次干净的 UI 栈重启,尽量把设备拉回正常首页/KUAL 可用状态。
|
||||
|
||||
stop_job() {
|
||||
job_name=$1
|
||||
stop "$job_name" >/dev/null 2>&1 || initctl stop "$job_name" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
start_job() {
|
||||
job_name=$1
|
||||
start "$job_name" >/dev/null 2>&1 || initctl start "$job_name" >/dev/null 2>&1 || true
|
||||
}
|
||||
|
||||
wait_for_cvm() {
|
||||
attempts=0
|
||||
while [ "$attempts" -lt 12 ]; do
|
||||
if pgrep -x cvm >/dev/null 2>&1; then
|
||||
return 0
|
||||
fi
|
||||
|
||||
attempts=$((attempts + 1))
|
||||
sleep 1
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
pkill -f dash.sh 2>/dev/null || true
|
||||
pkill -f start.sh 2>/dev/null || true
|
||||
|
||||
lipc-set-prop com.lab126.powerd preventScreenSaver 0 2>/dev/null || true
|
||||
|
||||
# 先把残留 UI 栈彻底停干净,避免 webreader 存活但 cvm 已崩的白屏状态。
|
||||
stop_job webreader
|
||||
stop_job framework
|
||||
killall cvm 2>/dev/null || true
|
||||
sleep 2
|
||||
|
||||
if [ -x /etc/init.d/framework ]; then
|
||||
/etc/init.d/framework start || true
|
||||
else
|
||||
start_job framework
|
||||
fi
|
||||
|
||||
# framework 拉起后,先等 cvm 真正起来,再启动 webreader。
|
||||
if ! wait_for_cvm; then
|
||||
echo "警告:framework 已请求启动,但 cvm 未在预期时间内出现。"
|
||||
fi
|
||||
|
||||
start_job webreader
|
||||
sleep 2
|
||||
|
||||
Reference in New Issue
Block a user