Как получить Плимут для отображения загрузочных сообщений при загрузке?

20

Я хотел бы знать, как создать заставку для Ubuntu Maverick с загрузочными сообщениями, отображаемыми при загрузке, а также с индикатором выполнения и вращающимся логотипом.

Или, если возможно, как отредактировать заставку, которая уже имеет вращающийся логотип и индикатор выполнения, и добавить к ней загрузочные сообщения.

Это тема, которую я хотел бы редактировать:

и я хотел бы что-то вроде этого:

введите описание изображения здесь

или это, именно то, что я хочу создать:

введите описание изображения здесь


Мне удалось найти этот сайт, на котором есть много полезной информации, но мне трудно понять некоторые из них. не делал никаких сценариев раньше!

Создайте свой собственный экран-заставку с прокруткой загрузочных сообщений

Сценарий Плимут

и это еще один сайт, который может помочь со сценариями

это скрипт для индикатора выполнения в заставке:

    #----------------------------------------- Progress Bar --------------------------------

    progress_box.image = Image("progress_box.png");
    progress_box.sprite = Sprite(progress_box.image);

    progress_box.x = Window.GetX() + Window.GetWidth() / 2 - progress_box.image.GetWidth() / 2;
    progress_box.y = Window.GetY() + Window.GetHeight() * 0.65 - progress_box.image.GetHeight() / 2;
    progress_box.sprite.SetPosition(progress_box.x, progress_box.y, 0);

    progress_bar.original_image = Image("progress_bar.png");
    progress_bar.sprite = Sprite();

    progress_bar.x = Window.GetX() + Window.GetWidth()  / 2 -        progress_bar.original_image.GetWidth() / 2;
    progress_bar.y = Window.GetY() + Window.GetHeight() * 0.65  - progress_box.image.GetHeight() / 2 + (progress_box.image.GetHeight() - progress_bar.original_image.GetHeight()) / 2;
    progress_bar.sprite.SetPosition(progress_bar.x, progress_bar.y, 1);

    fun progress_callback (duration, progress)
      {
        if (progress_bar.image.GetWidth () != Math.Int (progress_bar.original_image.GetWidth () * progress))
          {
# add the * 3 to multiply the speed of the progress bar by 3
            progress_bar.image = progress_bar.original_image.Scale(progress_bar.original_image.GetWidth(progress_bar.original_image) * progress * 3, progress_bar.original_image.GetHeight());
            progress_bar.sprite.SetImage (progress_bar.image);
          }
      }

    Plymouth.SetBootProgressFunction(progress_callback);

Хорошо, так что я сделал намного больше поиска, и я смог узнать немного больше о Плимуте.

это сайты, которые я нашел полезными. Руководство по темам Plymouth В этой статье есть четыре части, которые вы должны прочитать, чтобы понять суть того, что мы делаем. (Я сравнивал их сценарий со сценарием для моего всплеска, чтобы понять, что к чему, я рекомендую сделать это, если кто-то хочет следовать по моим стопам.) Сценарии в Плимуте Хорошо, по этой ссылке нужно пройти 2 страницы, чтобы понять, что делать , Оказывается, они следовали за тем же, что и я, то есть перенаправляли загрузочные сообщения на всплеск Плимута, где у меня уже есть вращающийся логотип, фон, индикатор выполнения.

Поэтому мне пришлось отредактировать мой /lib/lsb/init-functionsфайл и сделать так, чтобы он отправлял сообщения об ошибке / успешной загрузке и т. Д. В Плимут, добавив этот абзац

# SEND MESSAGES TO PLYMOUTH
if [ -x /bin/plymouth ] && pidof plymouthd >/dev/null
then
    plymouth_send() {
        [ "$1" = '-n' ] && { # add a flag '>' for lines that will be extended
            shift
            /bin/plymouth message --text=">$*" || true
            return
        }
        [ "$1" = '-w' ] && { # add "warning" formatting
            shift
            /bin/plymouth update --status="warning" || true
            /bin/plymouth message --text="$*" || true
            /bin/plymouth update --status="normal" || true
            return
        }
        [ "$1" = '-f' ] && { # add "failed" formatting
            shift
            /bin/plymouth update --status="failed" || true
            /bin/plymouth message --text="$*" || true
            /bin/plymouth update --status="normal" || true
            return
        }
        /bin/plymouth message --text="$*" || true
    }
else
    plymouth_send() { :; }
fi

# int log_begin_message (char *message)
log_begin_msg () {
    if [ -z "${1:-}" ]; then
        return 1
    fi
    echo -n "$@"
}

А также добавление

   # Only do the fancy stuff if we have an appropriate terminal
    # and if /usr is already mounted
    if log_use_fancy_output; then
        RED=`$TPUT setaf 1`
        YELLOW=`$TPUT setaf 3`
        NORMAL=`$TPUT op`
    else
        RED=''
        YELLOW=''
        NORMAL=''
    fi

    if [ $1 -eq 0 ]; then
        echo "."
        plymouth_send "."
    elif [ $1 -eq 255 ]; then
        /bin/echo -e " ${YELLOW}(warning).${NORMAL}"
        plymouth_send -w " (warning)."
    else
        /bin/echo -e " ${RED}failed!${NORMAL}"
        plymouth_send -f " failed!"
    fi
    log_end_msg_post "$@"
    return $retval
}

log_action_msg () {
    echo "$@."
    plymouth_send "$@."
}

log_action_begin_msg () {
    echo -n "$@..."
    plymouth_send -n "$@..."
}

log_action_cont_msg () {
    echo -n "$@..."
    plymouth_send -n "$@..."
}

log_action_end_msg () {
    log_action_end_msg_pre "$@"
    if [ -z "${2:-}" ]; then
        end="."
    else
        end=" ($2)."
    fi

    if [ $1 -eq 0 ]; then
        echo "done${end}"
        plymouth_send "done${end}"
    else
        if log_use_fancy_output; then
            RED=`$TPUT setaf 1`
            NORMAL=`$TPUT op`
            /bin/echo -e "${RED}failed${end}${NORMAL}"
        else
            echo "failed${end}"
            plymouth_send -f "failed${end}"
        fi
    fi
    log_action_end_msg_post "$@"
}

До сих пор я не мог передать сообщения в Плимут, но я лучше понял, как работает сценарий Плимута!

Я не знаю, что еще я должен сделать, чтобы это сработало! надеясь, что кто-то здесь может помочь мне

Ооо, и вот моя версия сценария для Splash, над которым я работаю.

# INT2MIL-Ubuntu-10.10-Eng splashy like theme

Window.GetMaxWidth = fun (){
  i = 0;
  width = 0;
  while (Window.GetWidth(i)){
    width = Math.Max(width, Window.GetWidth(i));
    i++;
    }
  return width;
};

Window.GetMaxHeight = fun (){
  i = 0;
  height = 0;
  while (Window.GetHeight(i)){
    height = Math.Max(height, Window.GetHeight(i));
    i++;
    }
  return height;
};

anim.imagecount = 100;
anim.target_width =  0.2* 0.46 * Window.GetWidth();
anim.target_height =  0.2* 0.46 * Window.GetWidth();

fun RotatedImage (index){
    index = Math.Int(index);
    if (!RotatedImageCache[index])
        RotatedImageCache[index] = anim.original_image.Rotate((Math.Pi*2*index)/anim.imagecount).Scale(anim.target_width, anim.target_height);

    return RotatedImageCache[index];
    }


if (Plymouth.GetMode() == "suspend" || Plymouth.GetMode() == "resume") {
  background.original_image = ImageNew("suspend.png");
  Window.SetBackgroundTopColor(1, 0, 0);
  Window.SetBackgroundBottomColor(0, 1, 0);
}
else {
  logo.original_image = ImageNew("logo.png");
  background.original_image = ImageNew("background.png");
  Window.SetBackgroundTopColor(0.234, 0.43, 0.705);
  Window.SetBackgroundBottomColor(0.16, 0.25, 0.44);

  anim.image= ImageNew("animation.png");
  anim.original_image= anim.image.Scale(anim.target_width, anim.target_width);

  anim.sprite = SpriteNew();
  anim.sprite.SetImage(RotatedImage (0));
  anim.sprite.SetX((Window.GetX() + Window.GetWidth() - RotatedImage(0).GetWidth()) / 2);
  anim.sprite.SetY(Window.GetY() + Window.GetHeight() * 0.37);
  anim.angle = 0;
  anim.index = 0;


}
#change reduction size to make logo bigger
ratio =  logo.original_image.GetWidth() / logo.original_image.GetHeight();
reduction = 0.4;
logo.image = logo.original_image.Scale(reduction * Window.GetMaxWidth() , reduction / ratio *  Window.GetMaxWidth());
logo.sprite = SpriteNew();
logo.sprite.SetImage(logo.image);
logo.opacity_angle = 0;
#change logo location
logo.sprite.SetX((Window.GetX() + Window.GetMaxWidth()  - logo.image.GetWidth())  / 2);
logo.sprite.SetY(Window.GetY() + Window.GetHeight() * 0.37);
#background image attributs x,z,y
background.image = background.original_image.Scale(Window.GetMaxWidth() , Window.GetMaxHeight());
background.sprite = SpriteNew();
background.sprite.SetImage(background.image);
background.sprite.SetPosition(Window.GetX(), Window.GetY(), -10);

sprite_prompt = SpriteNew();

fun refresh_callback ()
  {
    if (status == "normal")
      {
#anim.index=speed of rotation
    anim.index += 1;
    anim.index %= anim.imagecount;
    anim.sprite.SetImage(RotatedImage (anim.index));
        #anim.sprite.SetOpacity (1);
    motif.sprite.SetOpacity(motif.opacity);
      }
    else
      {
        anim.sprite.SetOpacity(0);
    motif.sprite.SetOpacity(0);
      }

  }

if (Plymouth.GetMode() != "suspend" && Plymouth.GetMode() != "resume") {
  Plymouth.SetRefreshFunction (refresh_callback);
}

#----------------------------------------- Dialog --------------------------------

status = "normal";

fun dialog_setup()
  {
    local.box;
    local.lock;
    local.entry;
    local.prompt_sprite;

    box.image = ImageNew("box.png");
    lock.image = ImageNew("lock.png");
    entry.image = ImageNew("entry.png");

    box.sprite = SpriteNew();
    box.sprite.SetImage(box.image);
    box.x = Window.GetX() + Window.GetWidth()  / 2 - box.image.GetWidth()/2;
    box.y = Window.GetY() + Window.GetHeight() / 2 - box.image.GetHeight()/2;
    box.z = 10000;
    box.sprite.SetPosition(box.x, box.y, box.z);

    lock.sprite = SpriteNew();
    lock.sprite.SetImage(lock.image);
    lock.x = box.x + box.image.GetWidth()/2 - (lock.image.GetWidth() + entry.image.GetWidth()) / 2;
    lock.y = box.y + box.image.GetHeight()/2 - lock.image.GetHeight()/2;
    lock.z = box.z + 1;
    lock.sprite.SetPosition(lock.x, lock.y, lock.z);

    entry.sprite = SpriteNew();
    entry.sprite.SetImage(entry.image);
    entry.x = lock.x + lock.image.GetWidth();
    entry.y = box.y + box.image.GetHeight()/2 - entry.image.GetHeight()/2;
    entry.z = box.z + 1;
    entry.sprite.SetPosition(entry.x, entry.y, entry.z);

    prompt_sprite = SpriteNew();
    prompt_sprite.SetPosition(box.x, box.y - 20, box.z);

    global.dialog.box = box;
    global.dialog.lock = lock;
    global.dialog.entry = entry;
    global.dialog.bullet_image = ImageNew("bullet.png");
    global.dialog.prompt_sprite = prompt_sprite;
    dialog_opacity (1);
  }

fun dialog_opacity(opacity)
  {
    dialog.box.sprite.SetOpacity(opacity);
    dialog.lock.sprite.SetOpacity(opacity);
    dialog.entry.sprite.SetOpacity(opacity);
    dialog.prompt_sprite.SetOpacity(opacity);
    for (index = 0; dialog.bullet[index]; index++)
      {
        dialog.bullet[index].sprite.SetOpacity(opacity);
      }
  }

fun display_normal_callback ()
  {
    global.status = "normal";
    if (global.dialog)
      dialog_opacity (0);
  }

fun display_password_callback (prompt, bullets)
  {
    global.status = "password";
    if (!global.dialog) 
    dialog_setup();
    else 
    dialog_opacity(1);
    motif.sprite.SetOpacity(0);
    anim.sprite.SetOpacity(0);

    dialog.prompt_sprite.SetImage(Image.Text(prompt, 1.0, 1.0, 1.0));
    for (index = 0; dialog.bullet[index] || index < bullets; index++)
      { 

        if (!dialog.bullet[index])
          {
            dialog.bullet[index].sprite = SpriteNew();
            dialog.bullet[index].sprite.SetImage(dialog.bullet_image);
            dialog.bullet[index].x = dialog.entry.x + index * dialog.bullet_image.GetWidth();
            dialog.bullet[index].y = dialog.entry.y + dialog.entry.image.GetHeight() / 2 - dialog.bullet_image.GetHeight() / 2;
            dialog.bullet[index].z = dialog.entry.z + 1;
            dialog.bullet[index].sprite.SetPosition(dialog.bullet[index].x, dialog.bullet[index].y, dialog.bullet[index].z);
          }
        if (index < bullets)
          dialog.bullet[index].sprite.SetOpacity(1);
        else
          dialog.bullet[index].sprite.SetOpacity(0);
      }
  }

fun display_message_callback (prompt)
  {

prompt = Image.Text(prompt,1.0, 1.0, 1.0);
sprite_prompt.SetImage(prompt);
sprite_prompt.SetPosition(Window.GetX() + (Window.GetWidth() - prompt.GetWidth()) / 2, Window.GetY() + Window.GetHeight() * 0.93, 2);
  }

/* instantiate dialog at startup, to ensure all icons are loaded in memory before initrd is unmounted, in case /usr isn't mounted yet */
dialog_setup(); dialog_opacity(0);
Plymouth.SetDisplayNormalFunction(display_normal_callback);
Plymouth.SetDisplayPasswordFunction(display_password_callback);
Plymouth.SetMessageFunction(display_message_callback);

#----------------------------------------- Progress Bar --------------------------------

progress_box.image = Image("progress_box.png");
progress_box.sprite = Sprite(progress_box.image);

progress_box.x = Window.GetX() + Window.GetWidth() / 2 - progress_box.image.GetWidth() / 2;
progress_box.y = Window.GetY() + Window.GetHeight() * 0.65 - progress_box.image.GetHeight() / 2;
progress_box.sprite.SetPosition(progress_box.x, progress_box.y, 0);

progress_bar.original_image = Image("progress_bar.png");
progress_bar.sprite = Sprite();

progress_bar.x = Window.GetX() + Window.GetWidth()  / 2 -        progress_bar.original_image.GetWidth() / 2;
progress_bar.y = Window.GetY() + Window.GetHeight() * 0.65  - progress_box.image.GetHeight() / 2 + (progress_box.image.GetHeight() - progress_bar.original_image.GetHeight()) / 2;
progress_bar.sprite.SetPosition(progress_bar.x, progress_bar.y, 1);

fun progress_callback (duration, progress)
  {
    if (progress_bar.image.GetWidth () != Math.Int (progress_bar.original_image.GetWidth () * progress))
      {
        progress_bar.image = progress_bar.original_image.Scale(progress_bar.original_image.GetWidth(progress_bar.original_image) * progress * 3, progress_bar.original_image.GetHeight());
        progress_bar.sprite.SetImage (progress_bar.image);
      }
  }

Plymouth.SetBootProgressFunction(progress_callback);

#----------------------------------------- Status Update --------------------------------

NUM_SCROLL_LINES = 5;
LINE_WIDTH = 55;
# width of one character
CHAR_WIDTH = 7;
# height of one character
CHAR_HEIGHT = 10;

msg_color = [0.5,0.5,0.5]; # msg_color is array

fun update_status_callback(sta) {
  if (sta == "failed") msg_color = [1,0,0];
  if (sta == "warning") msg_color = [0.8,0.8,0];
  if (sta == "normal") msg_color = [0.5,0.5,0.5];
}

fun StringLength(string) {
  index = 0;
  str = String(string);
  while(str.CharAt(index)) index++;
  return index;
}

// Initialising text images and their positions
// 20 is the height (including line spacing) of each line
for (i=0; i < NUM_SCROLL_LINES; i++) {
  lines[i]= Image.Text("",0,0,0);
  message_sprite[i] = Sprite();
  message_sprite[i].SetX (Window.GetX() + (screen_width / 2 ) - (LINE_WIDTH * CHAR_WIDTH / 2));
  message_sprite[i].SetY (Window.GetY() + (screen_height / 2) + (logo_height /2) +(logo_height * 1.2)+ (i * CHAR_HEIGHT * 2) );
  message_sprite[i].SetZ (10000);
}

pretext = String("");

fun scroll_message_callback(text) {

 nobreak = 0;
   if (text.CharAt(0) == ">") {    # "no linebreak" flag, like "-n"
       text = text.SubString(1, StringLength(text)); # remove ">" at front
       nobreak = 1;
   }

   if (pretext == "") {

       if (nobreak == 1) pretext = text;

   // Truncate the message if too long
   if (StringLength(text) > LINE_WIDTH) {
     text = text.SubString(0, LINE_WIDTH - 3);
     text += "...";
   }

   // Shift messages one up
   for (i = 0; i < NUM_SCROLL_LINES - 1; i++) {
     lines[i] = lines[i+1];
   }
 else {    # the previous message was flagged to have no linebreak

          // Truncate the message if too long
       if (StringLength(text) > LINE_WIDTH - 5) { # leave min. 5 for pretext
         text = text.SubString(0, LINE_WIDTH - 8);
         text += "...";
       }

          # Truncate the previous message if too long
       if (StringLength(pretext) > (LINE_WIDTH - StringLength(text))) {
         pretext = pretext.SubString(0, LINE_WIDTH - StringLength(text) - 3);
         pretext += "...";
       }

       text = pretext + text;

       if (nobreak == 1) pretext = text;
       else pretext = "";

   }

   // Create the image for the latest message
#  original script had "lines[i]"   
   lines[i] = Image.Text( text, 0.5, 0.5, 0.5);

   // Re-allocate the text images to sprites
   for (i = 0; i < NUM_SCROLL_LINES; i++) {
     message_sprite[i].SetImage(lines[i]);
   }
}

Plymouth.SetUpdateStatusFunction(scroll_message_callback);

# messages get added to updates
Plymouth.SetMessageFunction(scroll_message_callback);

#----------------------------------------- Quit --------------------------------

fun quit_callback ()
{
 anim.sprite.SetOpacity (0);
  if (Plymouth.GetMode() == "shutdown") {
   motif.sprite.SetOpacity(0);
  }
}

Plymouth.SetQuitFunction(quit_callback);

Итак, я предоставил почти всю необходимую информацию, если кто-то знаком с этим, дайте мне знать, что мне не хватает, чтобы передать загрузочные сообщения в Плимут. Благодарность

Виталий
источник
Если кто-то хочет протестировать заставку без перезапуска, это простой метод: «Установите пакет plymouth-x11. Запустите sudo plymouthd; судо плимут - шоу-всплеск; спать 10; sudo killall plymouthd ' askubuntu.com/questions/51078/…
Виталий
1
Ох, и еще одна вещь, я должен изменить GRUB_CMDLINE_LINUX_DEFAULT = "тихий всплеск" на GRUB_CMDLINE_LINUX_DEFAULT = "всплеск", чтобы сообщения отображались?
Виталий
freedesktop.org/wiki/Software/Plymouth/Scripts еще одна страница сценариев Plymouth. это очень помогает, если вы не знаете ни JavaScript, ни С.
Виталий

Ответы:

4

Итак, я работаю над этой проблемой уже 4 дня, и я почти полностью ее прибил. До сих пор я мог заставить Плимут загружаться с отображаемыми сообщениями, но, к сожалению, сообщения усекаются. Сейчас я пытаюсь настроить скрипты, но я не знаю, где проблема в скрипте / lib / lsb / init-functions или /lib/plymouth/themes/"theme-name"/mdv.script.

Вот моя работа до сих пор.

сначала вы должны заставить init-функции отправлять сообщения в Плимут, чтобы он выглядел следующим образом (просмотрите каждую строку, чтобы увидеть различия и скопируйте строку, соответствующую отправке в Плимут):

# /lib/lsb/init-functions for Debian -*- shell-script -*-
#
#Copyright (c) 2002-08 Chris Lawrence
#All rights reserved.
#
#Redistribution and use in source and binary forms, with or without
#modification, are permitted provided that the following conditions
#are met:
#1. Redistributions of source code must retain the above copyright
#   notice, this list of conditions and the following disclaimer.
#2. Redistributions in binary form must reproduce the above copyright
#   notice, this list of conditions and the following disclaimer in the
#   documentation and/or other materials provided with the distribution.
#3. Neither the name of the author nor the names of other contributors
#   may be used to endorse or promote products derived from this software
#   without specific prior written permission.
#
#THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
#IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
#ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
#LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
#CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
#SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
#BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
#OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
#EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

start_daemon () {
    local force nice pidfile exec i args
    force=0
    nice=0
    pidfile=/dev/null

    OPTIND=1
    while getopts fn:p: opt ; do
        case "$opt" in
            f)  force=1;;
            n)  nice="$OPTARG";;
            p)  pidfile="$OPTARG";;
        esac
    done

    shift $(($OPTIND - 1))
    if [ "$1" = '--' ]; then
        shift
    fi

    exec="$1"; shift

    args="--start --nicelevel $nice --quiet --oknodo"
    if [ $force = 1 ]; then
        /sbin/start-stop-daemon $args --chdir "$PWD" --startas $exec --pidfile /dev/null -- "$@"
    elif [ $pidfile ]; then
        /sbin/start-stop-daemon $args --chdir "$PWD" --exec $exec --oknodo --pidfile "$pidfile" -- "$@"
    else
        /sbin/start-stop-daemon $args --chdir "$PWD" --exec $exec -- "$@"
    fi
}

pidofproc () {
    local pidfile line i pids= status specified pid
    pidfile=
    specified=

    OPTIND=1
    while getopts p: opt ; do
        case "$opt" in
            p)  pidfile="$OPTARG"; specified=1;;
        esac
    done
    shift $(($OPTIND - 1))

    base=${1##*/}
    if [ ! "$specified" ]; then
        pidfile="/var/run/$base.pid"
    fi

    if [ -n "${pidfile:-}" -a -r "$pidfile" ]; then
        read pid < "$pidfile"
        if [ -n "${pid:-}" ]; then
            if $(kill -0 "${pid:-}" 2> /dev/null); then
                echo "$pid"
                return 0
            elif ps "${pid:-}" >/dev/null 2>&1; then
                echo "$pid"
                return 0 # program is running, but not owned by this user
            else
                return 1 # program is dead and /var/run pid file exists
            fi
        fi
    fi
    if [ -x /bin/pidof -a ! "$specified" ]; then
        status="0"
        /bin/pidof -o %PPID -x $1 || status="$?"
        if [ "$status" = 1 ]; then
            return 3 # program is not running
        fi
        return 0
    fi
    return 4 # Unable to determine status
}

# start-stop-daemon uses the same algorithm as "pidofproc" above.
killproc () {
    local pidfile sig status base i name_param is_term_sig
    pidfile=
    name_param=
    is_term_sig=no

    OPTIND=1
    while getopts p: opt ; do
        case "$opt" in
            p)  pidfile="$OPTARG";;
        esac
    done
    shift $(($OPTIND - 1))

    base=${1##*/}
    if [ ! $pidfile ]; then
        name_param="--name $base --pidfile /var/run/$base.pid"
    else
        name_param="--pidfile $pidfile"
    fi

    sig=$(echo ${2:-} | sed -e 's/^-\(.*\)/\1/')
    sig=$(echo $sig | sed -e 's/^SIG\(.*\)/\1/')
    if [ -z "$sig" -o "$sig" = 15 -o "$sig" = TERM ]; then
        is_term_sig=yes
    fi
    status=0
    if [ ! "$is_term_sig" = yes ]; then
        if [ -n "$sig" ]; then
            /sbin/start-stop-daemon --stop --signal "$sig" --quiet $name_param || status="$?"
        else
            /sbin/start-stop-daemon --stop --quiet $name_param || status="$?"
        fi
    else
        /sbin/start-stop-daemon --stop --quiet --oknodo $name_param || status="$?"
    fi
    if [ "$status" = 1 ]; then
        if [ -n "$sig" ]; then
            return 0
        fi
        return 3 # program is not running
    fi

    if [ "$status" = 0 -a "$is_term_sig" = yes -a "$pidfile" ]; then
        pidofproc -p "$pidfile" "$1" >/dev/null || rm -f "$pidfile"
    fi
    return 0
}

# Return LSB status
status_of_proc () {
    local pidfile daemon name status

    pidfile=
    OPTIND=1
    while getopts p: opt ; do
        case "$opt" in
            p)  pidfile="$OPTARG";;
        esac
    done
    shift $(($OPTIND - 1))

    if [ -n "$pidfile" ]; then
        pidfile="-p $pidfile"
    fi
    daemon="$1"
    name="$2"

    status="0"
    pidofproc $pidfile $daemon >/dev/null || status="$?"
    if [ "$status" = 0 ]; then
        log_success_msg "$name is running"
        return 0
    elif [ "$status" = 4 ]; then
        log_failure_msg "could not access PID file for $name"
        return $status
    else
        log_failure_msg "$name is not running"
        return $status
    fi
}

log_use_fancy_output () {
    TPUT=/usr/bin/tput
    EXPR=/usr/bin/expr
    if [ -t 1 ] && [ "x${TERM:-}" != "x" ] && [ "x${TERM:-}" != "xdumb" ] && [ -x $TPUT ] && [ -x $EXPR ] && $TPUT hpa 60 >/dev/null 2>&1 && $TPUT setaf 1 >/dev/null 2>&1; then
        [ -z $FANCYTTY ] && FANCYTTY=1 || true
    else
        FANCYTTY=0
    fi
    case "$FANCYTTY" in
        1|Y|yes|true)   true;;
        *)              false;;
    esac
}

log_success_msg () {
    if [ -n "${1:-}" ]; then
        log_begin_msg $@
    fi
    log_end_msg 0
}

log_failure_msg () {
    if [ -n "${1:-}" ]; then
        log_begin_msg $@ "..."
    fi
    log_end_msg 1 || true
}

log_warning_msg () {
    if [ -n "${1:-}" ]; then
        log_begin_msg $@ "..."
    fi
    log_end_msg 255 || true
}

#
# NON-LSB HELPER FUNCTIONS
#
# int get_lsb_header_val (char *scriptpathname, char *key)
get_lsb_header_val () {
        if [ ! -f "$1" ] || [ -z "${2:-}" ]; then
                return 1
        fi
        LSB_S="### BEGIN INIT INFO"
        LSB_E="### END INIT INFO"
        sed -n "/$LSB_S/,/$LSB_E/ s/# $2: \(.*\)/\1/p" $1
}

# SEND MESSAGES TO PLYMOUTH
if [ -x /bin/plymouth ] && pidof plymouthd >/dev/null
then
    plymouth_send() {
        [ "$1" = '-n' ] && { # add a flag '>' for lines that will be extended
            shift
            /bin/plymouth message --text=">$*" || true
            return
        }
        [ "$1" = '-w' ] && { # add "warning" formatting
            shift
            /bin/plymouth update --status="warning" || true
            /bin/plymouth message --text="$*" || true
            /bin/plymouth update --status="normal" || true
            return
        }
        [ "$1" = '-f' ] && { # add "failed" formatting
            shift
            /bin/plymouth update --status="failed" || true
            /bin/plymouth message --text="$*" || true
            /bin/plymouth update --status="normal" || true
            return
        }
        /bin/plymouth message --text="$*" || true
    }
else
    plymouth_send() { :; }
fi

# int log_begin_message (char *message)
log_begin_msg () {
    if [ -z "${1:-}" ]; then
        return 1
    fi
    echo -n "$@"
    plymouth_send -n "$@"
}

# Sample usage:
# log_daemon_msg "Starting GNOME Login Manager" "gdm"
#
# On Debian, would output "Starting GNOME Login Manager: gdm"
# On Ubuntu, would output " * Starting GNOME Login Manager..."
#
# If the second argument is omitted, logging suitable for use with
# log_progress_msg() is used:
#
# log_daemon_msg "Starting remote filesystem services"
#
# On Debian, would output "Starting remote filesystem services:"
# On Ubuntu, would output " * Starting remote filesystem services..."

log_daemon_msg () {
    if [ -z "${1:-}" ]; then
        return 1
    fi
    log_daemon_msg_pre "$@"

    if [ -z "${2:-}" ]; then
        echo -n "$1:"
        plymouth_send -n "$1:"
        return
    fi

    echo -n "$1: $2"
    plymouth_send -n "$1: $2"
    log_daemon_msg_post "$@"
}

# #319739
#
# Per policy docs:
#
#     log_daemon_msg "Starting remote file system services"
#     log_progress_msg "nfsd"; start-stop-daemon --start --quiet nfsd
#     log_progress_msg "mountd"; start-stop-daemon --start --quiet mountd
#     log_progress_msg "ugidd"; start-stop-daemon --start --quiet ugidd
#     log_end_msg 0
#
# You could also do something fancy with log_end_msg here based on the
# return values of start-stop-daemon; this is left as an exercise for
# the reader...
#
# On Ubuntu, one would expect log_progress_msg to be a no-op.
log_progress_msg () {
    if [ -z "${1:-}" ]; then
        return 1
    fi
    echo -n " $@"
    plymouth_send -n " $@"
}


# int log_end_message (int exitstatus)
log_end_msg () {
    # If no arguments were passed, return
    if [ -z "${1:-}" ]; then
        return 1
    fi

    retval=$1

    log_end_msg_pre "$@"

    # Only do the fancy stuff if we have an appropriate terminal
    # and if /usr is already mounted
    if log_use_fancy_output; then
        RED=`$TPUT setaf 1`
        YELLOW=`$TPUT setaf 3`
        NORMAL=`$TPUT op`
    else
        RED=''
        YELLOW=''
        NORMAL=''
    fi

    if [ $1 -eq 0 ]; then
        echo "."
        plymouth_send "."
    elif [ $1 -eq 255 ]; then
        /bin/echo -e " ${YELLOW}(warning).${NORMAL}"
        plymouth_send -w "warning"
    else
        /bin/echo -e " ${RED}failed!${NORMAL}"
        plymouth_send -f "failed"
    fi
    log_end_msg_post "$@"
    return $retval
}

log_action_msg () {
    echo "$@."
    plymouth_send "$@."
}

log_action_begin_msg () {
    echo -n "$@..."
    plymouth_send -n "$@..."
}

log_action_cont_msg () {
    echo -n "$@..."
    plymouth_send -n "$@..."
}

log_action_end_msg () {
    log_action_end_msg_pre "$@"
    if [ -z "${2:-}" ]; then
        end="."
    else
        end=" ($2)."
    fi

    if [ $1 -eq 0 ]; then
        echo "done${end}"
        plymouth_send "done${end}"
    else
        if log_use_fancy_output; then
            RED=`$TPUT setaf 1`
            NORMAL=`$TPUT op`
            /bin/echo -e "${RED}failed${end}${NORMAL}"
            plymouth_send -f "failed${end}"
        else
            echo "failed${end}"
            plymouth_send -f "failed${end}"
        fi
    fi
    log_action_end_msg_post "$@"
}

# Hooks for /etc/lsb-base-logging.sh
log_daemon_msg_pre () { :; }
log_daemon_msg_post () { :; }
log_end_msg_pre () { :; }
log_end_msg_post () { :; }
log_action_end_msg_pre () { :; }
log_action_end_msg_post () { :; }

FANCYTTY=
[ -e /etc/lsb-base-logging.sh ] && . /etc/lsb-base-logging.sh || true

Теперь, после того как вы добавили это в init-функции, вам нужно отредактировать вашу тему Plymouth mdv.script

Это моя последняя обновленная версия скрипта:

# INT2MIL-Ubuntu-10.10-Eng splashy like theme

Window.GetMaxWidth = fun (){
  i = 0;
  width = 0;
  while (Window.GetWidth(i)){
    width = Math.Max(width, Window.GetWidth(i));
    i++;
    }
  return width;
};

Window.GetMaxHeight = fun (){
  i = 0;
  height = 0;
  while (Window.GetHeight(i)){
    height = Math.Max(height, Window.GetHeight(i));
    i++;
    }
  return height;
};
#change animcount to increase/decrease speed of spinning arrows
anim.imagecount = 100;
anim.target_width =  0.2* 0.46 * Window.GetWidth();
anim.target_height =  0.2* 0.46 * Window.GetWidth();

fun RotatedImage (index){
    index = Math.Int(index);
    if (!RotatedImageCache[index])
        RotatedImageCache[index] = anim.original_image.Rotate((Math.Pi*2*index)/anim.imagecount).Scale(anim.target_width, anim.target_height);

    return RotatedImageCache[index];
    }


if (Plymouth.GetMode() == "suspend" || Plymouth.GetMode() == "resume") {
  background.original_image = ImageNew("suspend.png");
  Window.SetBackgroundTopColor(1, 0, 0);
  Window.SetBackgroundBottomColor(0, 1, 0);
}
else {
  logo.original_image = ImageNew("logo.png");
  background.original_image = ImageNew("background.png");
  Window.SetBackgroundTopColor(0.234, 0.43, 0.705);
  Window.SetBackgroundBottomColor(0.16, 0.25, 0.44);

  anim.image= ImageNew("animation.png");
  anim.original_image= anim.image.Scale(anim.target_width, anim.target_width);

  anim.sprite = SpriteNew();
  anim.sprite.SetImage(RotatedImage (0));
  anim.sprite.SetX((Window.GetX() + Window.GetWidth() - RotatedImage(0).GetWidth()) / 2);
  anim.sprite.SetY(Window.GetY() + Window.GetHeight() * 0.37);
  anim.angle = 0;
  anim.index = 0;


}
#change reduction size to make logo bigger
ratio =  logo.original_image.GetWidth() / logo.original_image.GetHeight();
reduction = 0.4;
logo.image = logo.original_image.Scale(reduction * Window.GetMaxWidth() , reduction / ratio *  Window.GetMaxWidth());
logo.sprite = SpriteNew();
logo.sprite.SetImage(logo.image);
logo.opacity_angle = 0;
#change logo location
logo.sprite.SetX((Window.GetX() + Window.GetMaxWidth()  - logo.image.GetWidth())  / 2);
logo.sprite.SetY(Window.GetY() + Window.GetHeight() * 0.37);
#background image attributs x,z,y
background.image = background.original_image.Scale(Window.GetMaxWidth() , Window.GetMaxHeight());
background.sprite = SpriteNew();
background.sprite.SetImage(background.image);
background.sprite.SetPosition(Window.GetX(), Window.GetY(), -100);

sprite_prompt = SpriteNew();

fun refresh_callback ()
  {
    if (status == "normal")
      {
#anim.index=speed of rotation
    anim.index += 1;
    anim.index %= anim.imagecount;
    anim.sprite.SetImage(RotatedImage (anim.index));
        #anim.sprite.SetOpacity (1);
    motif.sprite.SetOpacity(motif.opacity);
      }
    else
      {
        anim.sprite.SetOpacity(1);
    motif.sprite.SetOpacity(1);
      }

  }

if (Plymouth.GetMode() != "suspend" && Plymouth.GetMode() != "resume") {
  Plymouth.SetRefreshFunction (refresh_callback);
}

#----------------------------------------- Dialog --------------------------------

status = "normal";

fun dialog_setup()
  {
    local.box;
    local.lock;
    local.entry;
    local.prompt_sprite;

    box.image = ImageNew("box.png");
    lock.image = ImageNew("lock.png");
    entry.image = ImageNew("entry.png");

    box.sprite = SpriteNew();
    box.sprite.SetImage(box.image);
    box.x = Window.GetX() + Window.GetWidth()  / 2 - box.image.GetWidth()/2;
    box.y = Window.GetY() + Window.GetHeight() / 2 - box.image.GetHeight()/2;
    box.z = 10000;
    box.sprite.SetPosition(box.x, box.y, box.z);

    lock.sprite = SpriteNew();
    lock.sprite.SetImage(lock.image);
    lock.x = box.x + box.image.GetWidth()/2 - (lock.image.GetWidth() + entry.image.GetWidth()) / 2;
    lock.y = box.y + box.image.GetHeight()/2 - lock.image.GetHeight()/2;
    lock.z = box.z + 1;
    lock.sprite.SetPosition(lock.x, lock.y, lock.z);

    entry.sprite = SpriteNew();
    entry.sprite.SetImage(entry.image);
    entry.x = lock.x + lock.image.GetWidth();
    entry.y = box.y + box.image.GetHeight()/2 - entry.image.GetHeight()/2;
    entry.z = box.z + 1;
    entry.sprite.SetPosition(entry.x, entry.y, entry.z);

    prompt_sprite = SpriteNew();
    prompt_sprite.SetPosition(box.x, box.y - 20, box.z);

    global.dialog.box = box;
    global.dialog.lock = lock;
    global.dialog.entry = entry;
    global.dialog.bullet_image = ImageNew("bullet.png");
    global.dialog.prompt_sprite = prompt_sprite;
    dialog_opacity (1);
  }

fun dialog_opacity(opacity)
  {
    dialog.box.sprite.SetOpacity(opacity);
    dialog.lock.sprite.SetOpacity(opacity);
    dialog.entry.sprite.SetOpacity(opacity);
    dialog.prompt_sprite.SetOpacity(opacity);
    for (index = 0; dialog.bullet[index]; index++)
      {
        dialog.bullet[index].sprite.SetOpacity(opacity);
      }
  }

fun display_normal_callback ()
  {
    global.status = "normal";
    if (global.dialog)
      dialog_opacity (0);
  }

fun display_password_callback (prompt, bullets)
  {
    global.status = "password";
    if (!global.dialog) 
    dialog_setup();
    else 
    dialog_opacity(1);
    motif.sprite.SetOpacity(1);
    anim.sprite.SetOpacity(1);

    dialog.prompt_sprite.SetImage(Image.Text(prompt, 1.0, 1.0, 1.0));
    for (index = 0; dialog.bullet[index] || index < bullets; index++)
      { 

        if (!dialog.bullet[index])
          {
            dialog.bullet[index].sprite = SpriteNew();
            dialog.bullet[index].sprite.SetImage(dialog.bullet_image);
            dialog.bullet[index].x = dialog.entry.x + index * dialog.bullet_image.GetWidth();
            dialog.bullet[index].y = dialog.entry.y + dialog.entry.image.GetHeight() / 2 - dialog.bullet_image.GetHeight() / 2;
            dialog.bullet[index].z = dialog.entry.z + 1;
            dialog.bullet[index].sprite.SetPosition(dialog.bullet[index].x, dialog.bullet[index].y, dialog.bullet[index].z);
          }
        if (index < bullets)
          dialog.bullet[index].sprite.SetOpacity(1);
        else
          dialog.bullet[index].sprite.SetOpacity(0);
      }
  }

fun display_message_callback (prompt)
  {

prompt = Image.Text(prompt,1.0, 1.0, 1.0);
sprite_prompt.SetImage(prompt);
sprite_prompt.SetPosition(Window.GetX() + (Window.GetWidth() - prompt.GetWidth()) / 2, Window.GetY() + Window.GetHeight() * 0.93, 2);
  }

/* instantiate dialog at startup, to ensure all icons are loaded in memory before initrd is unmounted, in case /usr isn't mounted yet */
dialog_setup(); dialog_opacity(0);
Plymouth.SetDisplayNormalFunction(display_normal_callback);
Plymouth.SetDisplayPasswordFunction(display_password_callback);
Plymouth.SetMessageFunction(display_message_callback);

#----------------------------------------- Progress Bar --------------------------------

progress_box.image = Image("progress_box.png");
progress_box.sprite = Sprite(progress_box.image);

progress_box.x = Window.GetX() + Window.GetWidth() / 2 - progress_box.image.GetWidth() / 2;
progress_box.y = Window.GetY() + Window.GetHeight() * 0.65 - progress_box.image.GetHeight() / 2;
progress_box.sprite.SetPosition(progress_box.x, progress_box.y, 0);

progress_bar.original_image = Image("progress_bar.png");
progress_bar.sprite = Sprite();

progress_bar.x = Window.GetX() + Window.GetWidth()  / 2 -        progress_bar.original_image.GetWidth() / 2;
progress_bar.y = Window.GetY() + Window.GetHeight() * 0.65  - progress_box.image.GetHeight() / 2 + (progress_box.image.GetHeight() - progress_bar.original_image.GetHeight()) / 2;
progress_bar.sprite.SetPosition(progress_bar.x, progress_bar.y, 1);

fun progress_callback (duration, progress)
  {
    if (progress_bar.image.GetWidth () != Math.Int (progress_bar.original_image.GetWidth () * progress))
      {
# * 3 = multiply progress by 3
        progress_bar.image = progress_bar.original_image.Scale(progress_bar.original_image.GetWidth(progress_bar.original_image) * progress * 3, progress_bar.original_image.GetHeight());
        progress_bar.sprite.SetImage (progress_bar.image);
      }
  }

Plymouth.SetBootProgressFunction(progress_callback);

#----------------------------------------- Status Update --------------------------------

NUM_SCROLL_LINES=10;
LINE_WIDTH=55;

# width of one character doesnt work------------
CHAR_WIDTH = 7;
# height of one character
CHAR_HEIGHT = 10; 
#------------------------

msg_color = [1,1,1]; # msg_color is array

#status callback function

fun update_status_callback(sta) {
  if (sta == "failed") msg_color = [1,0,0];
  if (sta == "warning") msg_color = [0.8,0.8,0];
  if (sta == "normal") msg_color = [0.5,0.5,0.5];
}

screen_width = Window.GetWidth();
screen_height = Window.GetHeight();

#Initialising text images and their positions
# 20 is the height (including line spacing) of each line

for (i=0; i < NUM_SCROLL_LINES; i++) {
  lines[i]= Image.Text("", msg_color[0], msg_color[1], msg_color[2]);
  message_sprite[i] = Sprite();
  message_sprite[i].SetPosition(screen_width * 0.025, (screen_height * 0.6) + (i * 20), 10000);
}

fun StringLength(string) {

  index = 0;
  str = String(string);
  while(str.CharAt(index)) index++;
  return index;
}

pretext = String("");

#scroll message function

fun scroll_message_callback(text) {

 ##nobreak function 

  nobreak = 0;
   if (text.CharAt(0) == ">") {    # "no linebreak" flag, like "-n"
       text = text.SubString(1, StringLength(text)); # remove ">" at front
       nobreak = 1;
   }

   if ((pretext == "") || (StringLength(text) > 15))  {
   if (text == ".") return;    # ignore messages of only a single dot

       if (nobreak == 1) pretext = text;



#Truncate the message if too long


   if (StringLength(text) > LINE_WIDTH) {
     text = text.SubString(0, LINE_WIDTH - 0);
     text += "...";
   }




#Shift message one up

   for (i = 0; i < NUM_SCROLL_LINES - 1; i++) {
     lines[i] = lines[i+1];
   }


}
   else {    # the previous message was flagged to have no linebreak

          // Truncate the message if too long
       if (StringLength(text) > LINE_WIDTH - 5) { # leave min. 5 for pretext
         text = text.SubString(0, LINE_WIDTH - 8);
         text += "...";
       }

          # Truncate the previous message if too long
       if (StringLength(pretext) > (LINE_WIDTH - StringLength(text))) {
         pretext = pretext.SubString(0, LINE_WIDTH - StringLength(text) - 3);
         pretext += "...";
       }

       text = pretext + text;

       if (nobreak == 1) pretext = text;
       else pretext = ">";

   }


#Create the image for the latest message

     lines[i] = Image.Text(text, msg_color[0], msg_color[1], msg_color[2]);

#Re-positioning the text images

  for (i = 0; i < NUM_SCROLL_LINES; i++) {
     message_sprite[i].SetImage(lines[i]);
   }
}

 Plymouth.SetUpdateStatusFunction(update_status_callback);

 Plymouth.SetUpdateStatusFunction(scroll_message_callback);


#----------------------------------------- Quit --------------------------------

fun quit_callback ()
{
 anim.sprite.SetOpacity (1);
  if (Plymouth.GetMode() == "shutdown") {
   motif.sprite.SetOpacity(1);
  }
}

Plymouth.SetQuitFunction(quit_callback);

В основном сценарий может быть применен к любой теме, все, что вам нужно сделать, это предоставить имена файлов изображений в папке. И изменив несколько других строк, чтобы настроить изображения на экране. Или то, что вы делаете, вы копируете необходимую часть, например, скажем, вы хотите часть прогресса, поэтому все, что вам нужно сделать, это скопировать все из

----------------------------------------- Индикатор ------- -------------------------

пока

----------------------------------------- Обновление статуса ------- -------------------------

После того, как вы закончите редактирование mdv.script, обязательно sudo update-initramfs -u и при следующей загрузке вы увидите ваш новый всплеск.

Обязательно ознакомьтесь с ссылками, приведенными в моем вопросе, они очень информативны и помогут вам быстро понять сценарии Плимута.

Теперь, если вы сделали все, что я сказал здесь, ваш загрузочный всплеск должен отображать прокручивающиеся сообщения. Теперь об усеченной части, я сейчас работаю над ней, но это раздражает необходимость перезагрузки моей машины каждый раз, когда я делаю некоторые изменения. Можно ли проверить процесс загрузки, пока я вхожу в систему как

sudo plymouthd ; sudo plymouth --show-splash ; sudo plymouth update --status="Hello" ; sleep 2 ; sudo plymouth update --status="This is a test. This is a test. This is a test. This is a test. This is a test. This is a test. " ; sleep 10 ; sudo killall plymouthd

Другой способ проверить Plymouth - скопировать приведенную выше команду в текстовый файл и добавить, sudo plymouth update --status="your message" чтобы пролистать больше сообщений. Затем сделайте файл исполняемым и запустите в терминале.

Надеюсь, что это поможет кому-либо еще, желающему отредактировать их всплеск Плимута. Удачи!!!

Виталий
источник
0

я знаю, что это старая ветка, но я думаю, что знаю ответ об усечении текста или нет. В моем themename.script (не в файле themename.plymouth) я использую две переменные.

1. NUM_SCROLL_LINES
2. LINE_WIDTH

с помощью этих переменных я могу установить, сколько строк будет отображаться и как долго будут отображаться строки. В скрипте также есть функция, которая усекает строки, если они слишком длинные для отображения.

Я надеюсь, что это будет полезно для кого-то.

Моя проблема в opensuse 42.1 Прыжок есть. Plymouth не показывает загрузочные сообщения, как будто они записаны в /var/log/boot.log. Показывает системные вызовы для сервисов.

Я также редактировал / lib / lsb / init-functions таким образом

if [ -x /usr/bin/plymouth ]; then
  /usr/bin/plymouth update --status="$@"
fi

или это

[ -x /usr/bin/plymouth ] && /usr/bin/plymouth update --status="$@"

Ничто не сработало для меня. У кого-нибудь есть идея?

Вот themename.script, который я использую

NUM_SCROLL_LINES=52;
LINE_WIDTH=120;

wallpaper_image = Image("background.png");
screen_width = Window.GetWidth();
screen_height = Window.GetHeight();
resized_wallpaper_image = wallpaper_image.Scale(screen_width,screen_height);
wallpaper_sprite = Sprite(resized_wallpaper_image);
wallpaper_sprite.SetZ(-100);

// Initialising text images and their positions
// 20 is the height (including line spacing) of each line
for (i=0; i < NUM_SCROLL_LINES; i++) {
  lines[i]= Image.Text("", 0, 0, 0);
  message_sprite[i] = Sprite();
#  here you can set the hights and width of textdisplay. 0.005 uses almost the whole screen.
#  message_sprite[i].SetPosition(screen_width * 0.2, (screen_height * 0.6) + (i * 20), 10000);
  message_sprite[i].SetPosition(screen_width * 0.005, (screen_height * 0.005) + (i * 20), 10000);
}

// From ubuntu-logo
fun StringLength(string) {
  index = 0;
  str = String(string);
  while(str.CharAt(index)) index++;
    return index;
}

fun scroll_message_callback(text) {
  // Truncate the message if too long
  if (StringLength(text) > LINE_WIDTH) {
    text = text.SubString(0, LINE_WIDTH - 3);
    text += "...";
  }

  // Shift message one up
  for (i = 0; i < NUM_SCROLL_LINES - 1; i++) {
    lines[i] = lines[i+1];
  }

  // Create the image for the latest message
  # z.B. turquoise coloured text / (text, 0.28, 0.82, 0.80); // 1.0 = 100% 0.28 = 28% usw.
  lines[i] = Image.Text(text, 0.28, 0.82, 0.80);

  // Re-positioning the text images
  for (i = 0; i < NUM_SCROLL_LINES; i++) {
    message_sprite[i].SetImage(lines[i]);
  }

}

Plymouth.SetUpdateStatusFunction(scroll_message_callback);
user572252
источник