| #!/bin/sh |
| # Copyright (c) 2011 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| # |
| # Converts and displays UTF-8 based text message file from |
| # /usr/share/chromeos-assets/text/boot_messages/${locale}/${message}.txt . |
| # Uses pango-view to convert from txt to png, then frecon to render png on |
| # frame buffer. |
| |
| # Default conversion parameters (can be overridden by environment variables) |
| # Note pango-view and frecon uses different color code format (#rrggbb vs |
| # 0xrrggbb), so we use format 'rrggbb' in IMAGE_BACKGROUND_RGB and not |
| # allowing common names. |
| : ${IMAGE_BACKGROUND_RGB:=202124} |
| : ${IMAGE_FONT_NAME:=sans-serif} |
| : ${IMAGE_FONT_SIZE:=22} |
| : ${IMAGE_MARGIN_SIZE:=5} |
| : ${IMAGE_TEXT_COLOR:=White} |
| : ${MESSAGE_BASE_PATH:=/usr/share/chromeos-assets/text/boot_messages} |
| : ${ASSETS_IMAGE_PATH:=/usr/share/chromeos-assets/images} |
| : ${SPINNER_IMAGE_BASE:=/usr/share/chromeos-assets/images/spinner/48x48} |
| : ${SPINNER_INTERVAL:=100} |
| : ${PROGRESS_BAR_WIDTH:=2} |
| : ${PROGRESS_BAR_RGB:=d0d0d0} |
| : ${DISPLAY_INDICATOR_FILE:=/run/boot_message_displayed} |
| : ${LOGGER_TAG:=display_boot_message} |
| : ${MESSAGE_OPTIONS:=""} |
| |
| isatty() { |
| local path="$1" |
| [ -t 0 ] <"${path}" |
| } |
| |
| create_temp_output() { |
| # File extension (.png) is required for render engine (pango-view) to decide |
| # output format. |
| mktemp --suffix .png |
| } |
| |
| # Prints width of given PNG file. |
| get_png_width() { |
| local input="$1" |
| local width |
| # Get image width (the 17~20 bytes of PNG file in big-endian). |
| width="$(dd if="${input}" bs=1 count=4 skip=16 2>/dev/null | od -A n -t x1)" |
| echo "$(( 0x$(echo "${width}" | sed 's/ //g') ))" |
| } |
| |
| # Converts a text file to PNG file by pango-view without further postprocessing. |
| txt_to_png() { |
| local input="$1" |
| local output="$2" |
| local locale="$3" |
| local option="$4" |
| pango-view -q --output="${output}" \ |
| --dpi=72 --align=left --hinting=full \ |
| --margin="${IMAGE_MARGIN_SIZE}"\ |
| --font="${IMAGE_FONT_NAME} ${IMAGE_FONT_SIZE}" \ |
| --foreground="${IMAGE_TEXT_COLOR}" \ |
| --background="#${IMAGE_BACKGROUND_RGB}" \ |
| --language="${locale}" \ |
| ${option} "${input}" |
| } |
| |
| # Converts a message file to PNG format to fit into given size. |
| message_to_png() { |
| local input="$1" |
| local output="$2" |
| local locale="$3" |
| local max_size_file="$4" |
| local extra_options="$5" |
| |
| txt_to_png "${input}" "${output}" "${locale}" "${extra_options}" |
| |
| # We prefer a "left-aligned text image on center of screen, with text |
| # wrapped by margin of its background image (max_size_file)". However if a |
| # --width is assigned to pango-view, it will always pad (by text align |
| # direction) to specified width, even if the image is smaller. That creates an |
| # image which is always aligned to left of background, not on center. To fix |
| # that, we first create the file, compare the width, and assign --width only |
| # if we need wrapping. |
| if [ -f "${max_size_file}" ]; then |
| local max_width="$(get_png_width "${max_size_file}")" |
| local width="$(get_png_width "${output}")" |
| if [ "${max_width}" -gt 0 -a "${width}" -gt "${max_width}" ]; then |
| extra_options="${extra_options} --width=${max_width}" |
| txt_to_png "${input}" "${output}" "${locale}" "${extra_options}" |
| fi |
| fi |
| } |
| |
| # Returns if given message needs spinner animation. |
| need_spinner() { |
| local message="$1" |
| |
| case "${message}" in |
| enter_dev2 | leave_dev | self_repair | update_firmware | power_wash | \ |
| show_spinner | wipe | update_detachable_base_firmware | \ |
| update_touchpad_firmware | update_touchscreen_firmware | \ |
| update_firmware_slow_extra_warning | update_fwupd_firmware) |
| return 0 |
| ;; |
| *) |
| # Default: don't show spinner for unknown messages |
| return 1 |
| esac |
| } |
| |
| # Returns true if given message requires frecon to be restarted. |
| need_frecon_restart() { |
| local message="$1" |
| |
| # Restart frecon if needs spinner. |
| if need_spinner "${message}"; then |
| return 0 |
| fi |
| # Restart frecon if not running. |
| if ! pgrep frecon > /dev/null; then |
| return 0 |
| fi |
| # Restart frecon if /run/frecon/vt0 does not exist. |
| if ! isatty /run/frecon/vt0; then |
| return 0 |
| fi |
| # Do not restart frecon for the next messages: |
| case "${message}" in update_arc_data_snapshot) |
| return 1 |
| ;; |
| *) |
| return 0 |
| esac |
| } |
| |
| # Renders given images to screen. |
| render_images() { |
| local rc=0 |
| local message="$1" |
| local file="$2" |
| local spiner_offset="" |
| local spinner_file="${SPINNER_IMAGE_BASE}"*01.png |
| |
| # Keep a copy of file in temp folder and never delete so it won't be removed |
| # before the renderer (frecon running in background) is ready. |
| # This is required for factory wiping that file lives on stateful partition. |
| # It will be removed only when system reboots. |
| local image_file="$(create_temp_output)" |
| cp -f "${file}" "${image_file}" |
| |
| # Put spinner in left of centered message file, with a padding of 1/2 spinner |
| # image width (offset is calculated from the default location, which puts |
| # spinner in center). |
| local spinner_offset_x=-"$(( $(get_png_width "${image_file}") / 2 + |
| $(get_png_width ${spinner_file}) ))" |
| |
| # TODO(hungte) Figure out a way to render using OSC in frecon if there is |
| # already a frecon instance running. |
| # Stop any running frecon instance. |
| touch "${DISPLAY_INDICATOR_FILE}" |
| |
| # By default frecon would exit after all images are displayed unless there |
| # is --loop-start or --enable-vts. Most boot-time messages need to keep the |
| # display until reboot so we want to always add --enable-vts. |
| |
| local params="--enable-vts \ |
| --clear 0x${IMAGE_BACKGROUND_RGB} \ |
| --frame-interval 0 \ |
| --scale=0 \ |
| --enable-vt1 \ |
| --no-login \ |
| --enable-osc \ |
| --pre-create-vts \ |
| ${ASSETS_IMAGE_PATH}/boot_message.png \ |
| ${image_file}" |
| |
| # Frecon requires all options specified before image files. |
| if need_spinner "${message}"; then |
| params="\ |
| --loop-offset ${spinner_offset_x},0 \ |
| --loop-interval ${SPINNER_INTERVAL} \ |
| --loop-start 2 \ |
| ${params} \ |
| ${SPINNER_IMAGE_BASE}*.png" |
| fi |
| |
| if need_frecon_restart "${message}"; then |
| logger -t "${LOGGER_TAG}" "Kill frecon and display message: ${message}" |
| pkill -9 frecon |
| |
| frecon --daemon ${params} |
| simulate_tty |
| else |
| logger -t "${LOGGER_TAG}" "Display message: ${message}" |
| |
| update_image "${ASSETS_IMAGE_PATH}/boot_message.png" |
| update_image "${image_file}" |
| fi |
| |
| return ${rc} |
| } |
| |
| # Shows an arbitrary text file. |
| show_text_file() { |
| local message="$1" |
| local locale="$2" |
| local file="$3" |
| local options="$4" |
| local rc |
| |
| # Some systems run the rendering in background (for example, frecon) and we |
| # don't know when the file will be accessed; so keep the file undeleted until |
| # next boot. |
| local output="$(create_temp_output)" |
| message_to_png "${file}" "${output}" "${locale}" \ |
| "${ASSETS_IMAGE_PATH}/boot_message.png" "${options}" && |
| render_images "${message}" "${output}" || rc=$? |
| rm -f "${output}" |
| return ${rc} |
| } |
| |
| # Shows a predefined and localized message from ${MESSAGE_BASE_PATH}. |
| show_message() { |
| local message="$1" |
| local locales="$2" |
| local options="${MESSAGE_OPTIONS}" |
| local rc=0 |
| local locale file |
| |
| for locale in ${locales}; do |
| file="${MESSAGE_BASE_PATH}/${locale}/${message}.txt" |
| [ -f "${file}" ] || continue |
| show_text_file "${message}" "${locale}" "${file}" "${options}" || rc=$? |
| return ${rc} |
| done |
| # If none of given locales have this message, return as failure. |
| logger -t "${LOGGER_TAG}" \ |
| "Couldn't find message ${message} in '${locales}'; not displaying anything." |
| return 1 |
| } |
| |
| # Shows a PNG image file or text markup file. |
| show_file() { |
| local message="$1" |
| local file="$2" |
| |
| case "${file}" in |
| *.png) |
| render_images "${message}" "${file}" |
| ;; |
| |
| *) |
| show_text_file "${message}" "" "${file}" "--markup" |
| ;; |
| esac |
| } |
| |
| # Draw a box using frecon box drawing escape sequences. |
| draw_box() { |
| local width="$1" |
| local height="$2" |
| local color="$3" |
| local offset_x="$4" |
| local offset_y="$5" |
| |
| # Bail out if the frecon terminal doesn't exist. |
| isatty /run/frecon/vt0 || return 1 |
| |
| printf '\033]box:color=0x%s;size=%u,%u;offset=%d,%d;scale=0\033\\' \ |
| "${color}" "${width}" "${height}" "${offset_x}" "${offset_y}" \ |
| > /run/frecon/vt0 |
| } |
| |
| # Show image using frecon image showing escape sequences. |
| update_image() { |
| local image_file="$1" |
| |
| isatty /run/frecon/vt0 || return 1 |
| |
| printf '\033]image:file=%s;color=%s;scale=0\033\\' "${image_file}" \ |
| "0x${IMAGE_BACKGROUND_RGB}" > /run/frecon/vt0 |
| } |
| |
| # Updates the progress bar. This requires frecon to already be set up, e.g. by |
| # previously invoking display_boot_message to show a message. |
| update_progress() { |
| local progress="$1" |
| |
| # Draw the frame. The calculations below size the bar to be half the width of |
| # the maximum message width, and 20 times as wide as high. The bar should show |
| # below the message, so it's offset by 5 times its height to the bottom. |
| local width="$(get_png_width "${ASSETS_IMAGE_PATH}/boot_message.png")" |
| local bar_width="$(( width / ${PROGRESS_BAR_WIDTH} ))" |
| local bar_height="$(( width / 40 ))" |
| local bar_offset_y="$(( bar_height * 5 ))" |
| |
| # The frame is created by a solid rectangle of the full bar dimension, |
| # overlayed with a slightly smaller rectangle in background color (2 pixels on |
| # each edge) to punch out the interior. |
| draw_box "${bar_width}" "${bar_height}" \ |
| "${PROGRESS_BAR_RGB}" "0" "${bar_offset_y}" |
| draw_box "$(( bar_width - 4 ))" "$(( bar_height - 4 ))" \ |
| "${IMAGE_BACKGROUND_RGB}" "0" "${bar_offset_y}" |
| |
| # Draw the progress bar. |
| if [ "$((progress))" -lt 0 ]; then |
| progress=0 |
| fi |
| if [ "$((progress))" -gt 100 ]; then |
| progress=100 |
| fi |
| |
| # The progress rectangle itself is slightly smaller than the frame (2 pixels |
| # on each edge), and needs to be offset to the left so it doesn't show at the |
| # center of the bar, but aligned with the left edge. |
| local progress_width="$(( ((bar_width - 8) * progress) / 100 ))" |
| local progress_offset=-"$(( (bar_width - 8 - progress_width) / 2 ))" |
| draw_box "${progress_width}" "$(( bar_height - 8 ))" \ |
| "${PROGRESS_BAR_RGB}" "${progress_offset}" "${bar_offset_y}" |
| } |
| |
| # Simulate the TTY1 for text output (for example, progress bar). |
| # Some legacy scripts may create a broken TTY1 so we want to delete that if |
| # it is not a character device. Note the vtN in /run/frecon starts from 0. |
| simulate_tty() { |
| local i="" frecon_vt="/run/frecon/vt" tty="" |
| for i in 1 2 3; do |
| tty="/dev/tty$((i + 1))" |
| if isatty "${frecon_vt}${i}" && ! isatty "${tty}"; then |
| rm -f "${tty}" |
| ln -sf "${frecon_vt}${i}" "${tty}" |
| fi |
| done |
| } |
| |
| action() { |
| local action="$1" |
| case "${action}" in |
| # Restores frecon when the boot message should be cleared. |
| restore_frecon) |
| if [ -f "${DISPLAY_INDICATOR_FILE}" ]; then |
| logger -t "${LOGGER_TAG}" "Kill and restore frecon." |
| rm "${DISPLAY_INDICATOR_FILE}" |
| pkill -9 frecon || true |
| if is_developer_end_user; then |
| # Restore frecon with the same arguments as boot-splash.conf |
| frecon --daemon --enable-osc --enable-vts --pre-create-vts |
| fi |
| fi |
| ;; |
| *) |
| return 1 |
| ;; |
| esac |
| } |
| |
| usage() { |
| cat <<EOL >&2 |
| Usage: $0 [MESSAGE LOCALES | show_file FILE | show_spinner FILE | |
| update_progress PERCENTAGE | |
| action [clear_indicator | restore_frecon] ]" |
| EOL |
| exit 1 |
| } |
| |
| main() { |
| [ "$#" = "2" ] || usage |
| |
| case "$1" in |
| show_file|show_spinner) |
| show_file "$1" "$2" |
| ;; |
| update_progress) |
| update_progress "$2" |
| ;; |
| action) |
| action "$2" |
| ;; |
| *) |
| show_message "$1" "$2" |
| ;; |
| esac |
| } |
| |
| set -e |
| main "$@" |