From 448009239664b2e851487b7dbfbc8bf4a9a02d27 Mon Sep 17 00:00:00 2001 From: Julien Lavergne Date: Wed, 10 Jan 2018 21:29:18 +0100 Subject: [PATCH] Add structure for new plymouth theme --- debian/changelog | 1 + .../plymouth-theme-lubuntu-next-logo.install | 1 + .../plymouth-theme-lubuntu-next-logo.postinst | 33 + debian/plymouth-theme-lubuntu-next-logo.prerm | 43 + .../plymouth-theme-lubuntu-next-text.install | 1 + .../plymouth-theme-lubuntu-next-text.postinst | 33 + debian/plymouth-theme-lubuntu-next-text.prerm | 43 + .../lubuntu-next-logo/lubuntu-logo.plymouth | 8 + .../lubuntu-next-logo/lubuntu-logo.script | 1072 +++++++++++++++++ .../themes/lubuntu-next-logo/lubuntu_logo.png | Bin 0 -> 22106 bytes .../lubuntu-next-logo/password_field.png | Bin 0 -> 1078 bytes .../lubuntu-next-logo/progress_dot_off.png | Bin 0 -> 421 bytes .../lubuntu-next-logo/progress_dot_on.png | Bin 0 -> 568 bytes .../lubuntu-next-text.plymouth | 11 + 14 files changed, 1246 insertions(+) create mode 100644 debian/plymouth-theme-lubuntu-next-logo.install create mode 100644 debian/plymouth-theme-lubuntu-next-logo.postinst create mode 100644 debian/plymouth-theme-lubuntu-next-logo.prerm create mode 100644 debian/plymouth-theme-lubuntu-next-text.install create mode 100644 debian/plymouth-theme-lubuntu-next-text.postinst create mode 100644 debian/plymouth-theme-lubuntu-next-text.prerm create mode 100644 src/usr/share/plymouth/themes/lubuntu-next-logo/lubuntu-logo.plymouth create mode 100644 src/usr/share/plymouth/themes/lubuntu-next-logo/lubuntu-logo.script create mode 100644 src/usr/share/plymouth/themes/lubuntu-next-logo/lubuntu_logo.png create mode 100644 src/usr/share/plymouth/themes/lubuntu-next-logo/password_field.png create mode 100644 src/usr/share/plymouth/themes/lubuntu-next-logo/progress_dot_off.png create mode 100644 src/usr/share/plymouth/themes/lubuntu-next-logo/progress_dot_on.png create mode 100644 src/usr/share/plymouth/themes/lubuntu-next-text/lubuntu-next-text.plymouth diff --git a/debian/changelog b/debian/changelog index ffc2d60..562a55c 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,7 @@ lubuntu-artwork (0.70) UNRELEASED; urgency=medium * Backup current artwork items to 1710 items, and prepare 1804 ones. + * Add new plymouth theme for Lubuntu Next. -- Julien Lavergne Wed, 10 Jan 2018 21:02:39 +0100 diff --git a/debian/plymouth-theme-lubuntu-next-logo.install b/debian/plymouth-theme-lubuntu-next-logo.install new file mode 100644 index 0000000..b9d5355 --- /dev/null +++ b/debian/plymouth-theme-lubuntu-next-logo.install @@ -0,0 +1 @@ +usr/share/plymouth/themes/lubuntu-next-logo/ diff --git a/debian/plymouth-theme-lubuntu-next-logo.postinst b/debian/plymouth-theme-lubuntu-next-logo.postinst new file mode 100644 index 0000000..8addb13 --- /dev/null +++ b/debian/plymouth-theme-lubuntu-next-logo.postinst @@ -0,0 +1,33 @@ +#!/bin/sh -e +# This script can be called in the following ways: +# +# After the package was installed: +# configure +# +# +# If prerm fails during upgrade or fails on failed upgrade: +# abort-upgrade +# +# If prerm fails during deconfiguration of a package: +# abort-deconfigure in-favour +# removing +# +# If prerm fails during replacement due to conflict: +# abort-remove in-favour + + +case "$1" in + configure) + update-alternatives \ + --install /usr/share/plymouth/themes/default.plymouth default.plymouth \ + /usr/share/plymouth/themes/lubuntu-next-logo/lubuntu-next-logo.plymouth 150 + + if which update-initramfs >/dev/null 2>&1 + then + update-initramfs -u + fi + ;; +esac + +#DEBHELPER# +exit 0 diff --git a/debian/plymouth-theme-lubuntu-next-logo.prerm b/debian/plymouth-theme-lubuntu-next-logo.prerm new file mode 100644 index 0000000..038ccf4 --- /dev/null +++ b/debian/plymouth-theme-lubuntu-next-logo.prerm @@ -0,0 +1,43 @@ +#!/bin/sh -e +# This script can be called in the following ways: +# +# After the package was removed: +# remove +# +# After the package was purged: +# purge +# +# After the package was upgraded: +# upgrade +# if that fails: +# failed-upgrade +# +# +# After all of the packages files have been replaced: +# disappear +# +# +# If preinst fails during install: +# abort-install +# +# If preinst fails during upgrade of removed package: +# abort-install +# +# If preinst fails during upgrade: +# abort-upgrade + + +case "$1" in + remove) + update-alternatives \ + --remove default.plymouth /usr/share/plymouth/themes/lubuntu-next-logo/lubuntu-next-logo.plymouth + + if which update-initramfs >/dev/null 2>&1 + then + update-initramfs -u + fi + ;; +esac + +#DEBHELPER# +exit 0 diff --git a/debian/plymouth-theme-lubuntu-next-text.install b/debian/plymouth-theme-lubuntu-next-text.install new file mode 100644 index 0000000..c19cf4c --- /dev/null +++ b/debian/plymouth-theme-lubuntu-next-text.install @@ -0,0 +1 @@ +usr/share/plymouth/themes/lubuntu-next-text/ diff --git a/debian/plymouth-theme-lubuntu-next-text.postinst b/debian/plymouth-theme-lubuntu-next-text.postinst new file mode 100644 index 0000000..335d3c0 --- /dev/null +++ b/debian/plymouth-theme-lubuntu-next-text.postinst @@ -0,0 +1,33 @@ +#!/bin/sh -e +# This script can be called in the following ways: +# +# After the package was installed: +# configure +# +# +# If prerm fails during upgrade or fails on failed upgrade: +# abort-upgrade +# +# If prerm fails during deconfiguration of a package: +# abort-deconfigure in-favour +# removing +# +# If prerm fails during replacement due to conflict: +# abort-remove in-favour + + +case "$1" in + configure) + update-alternatives \ + --install /usr/share/plymouth/themes/text.plymouth text.plymouth \ + /usr/share/plymouth/themes/lubuntu-next-text/lubuntu-next-text.plymouth 150 + + if which update-initramfs >/dev/null 2>&1 + then + update-initramfs -u + fi + ;; +esac + +#DEBHELPER# +exit 0 diff --git a/debian/plymouth-theme-lubuntu-next-text.prerm b/debian/plymouth-theme-lubuntu-next-text.prerm new file mode 100644 index 0000000..c88b007 --- /dev/null +++ b/debian/plymouth-theme-lubuntu-next-text.prerm @@ -0,0 +1,43 @@ +#!/bin/sh -e +# This script can be called in the following ways: +# +# After the package was removed: +# remove +# +# After the package was purged: +# purge +# +# After the package was upgraded: +# upgrade +# if that fails: +# failed-upgrade +# +# +# After all of the packages files have been replaced: +# disappear +# +# +# If preinst fails during install: +# abort-install +# +# If preinst fails during upgrade of removed package: +# abort-install +# +# If preinst fails during upgrade: +# abort-upgrade + + +case "$1" in + remove) + update-alternatives \ + --remove text.plymouth /usr/share/plymouth/themes/lubuntu-next-text/lubuntu-next-text.plymouth + + if which update-initramfs >/dev/null 2>&1 + then + update-initramfs -u + fi + ;; +esac + +#DEBHELPER# +exit 0 diff --git a/src/usr/share/plymouth/themes/lubuntu-next-logo/lubuntu-logo.plymouth b/src/usr/share/plymouth/themes/lubuntu-next-logo/lubuntu-logo.plymouth new file mode 100644 index 0000000..b223bdc --- /dev/null +++ b/src/usr/share/plymouth/themes/lubuntu-next-logo/lubuntu-logo.plymouth @@ -0,0 +1,8 @@ +[Plymouth Theme] +Name=Lubuntu Logo +Description=A theme that features a blank background with a logo. +ModuleName=script + +[script] +ImageDir=/usr/share/plymouth/themes/lubuntu-next-logo +ScriptFile=/usr/share/plymouth/themes/lubuntu-next-logo/lubuntu-logo.script diff --git a/src/usr/share/plymouth/themes/lubuntu-next-logo/lubuntu-logo.script b/src/usr/share/plymouth/themes/lubuntu-next-logo/lubuntu-logo.script new file mode 100644 index 0000000..9a6f26a --- /dev/null +++ b/src/usr/share/plymouth/themes/lubuntu-next-logo/lubuntu-logo.script @@ -0,0 +1,1072 @@ +# lubuntu-logo.script - boot splash plugin +# +# Copyright (C) 2009 Canonical Ltd. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. +# +# Re-written by Rafael Laguna based from +# original by Alberto Milone +# +# Based on the example provided with the "script plugin" written by: +# Charlie Brej +# + +# Set the text colour in (rgb / 256) +text_colour.red = 1.0; +text_colour.green = 1.0; +text_colour.blue = 1.0; + +# Tinted text #988592 +tinted_text_colour.red = 0.59; +tinted_text_colour.green = 0.52; +tinted_text_colour.blue = 0.57; + +# Action Text - #ffffff - RGB 255 255 255 +action_text_colour.red = 1.0; +action_text_colour.green = 1.0; +action_text_colour.blue = 1.0; + +# Orange - #ff4012 - RGB 255 64 18 +debugsprite = Sprite(); +debugsprite_bottom = Sprite(); +debugsprite_medium = Sprite(); + +# are we currently prompting for a password? +prompt_active = 0; + +# General purpose function to create text +fun WriteText (text, colour) { + image = Image.Text (text, colour.red, colour.green, colour.blue); + return image; +} + +fun ImageToText (text) { + image = WriteText (text, text_colour); + return image; +} + +fun ImageToTintedText (text) { + image = WriteText (text, tinted_text_colour); + return image; +} + +fun ImageToActionText (text) { + image = WriteText (text, action_text_colour); + return image; +} + +fun Debug(text) { + debugsprite.SetImage(ImageToText (text)); +} + +fun DebugBottom(text) { + debugsprite_bottom.SetImage(ImageToText (text)); + debugsprite_bottom.SetPosition(0, (Window.GetHeight (0) - 20), 1); +} + +fun DebugMedium(text) { + debugsprite_medium.SetImage(ImageToText (text)); + debugsprite_medium.SetPosition(0, (Window.GetHeight (0) - 60), 1); +} + +fun TextYOffset() { + local.y; + local.text_height; + local.min_height; + + # Put the 1st line below the logo + some spacing + y = logo.y + logo.height + (progress_indicator.bullet_height * 7 ); # + logo_spacing; + + text_height = first_line_height * 7.5; + + min_height = Window.GetHeight(); + if (y + text_height > min_height) + y = min_height - text_height; + + if (y < progress_indicator.y + progress_indicator.height) + return progress_indicator.y + progress_indicator.height; + return y; +} + +#------------------------------String functions------------------------------- + +# This is the equivalent for strstr() +fun StringString(string, substring) { + start = 0; + while (String(string).CharAt (start)) { + walk = 0; + while (String(substring).CharAt (walk) == String(string).CharAt (start + walk) ) { + walk++; + if (!String(substring).CharAt (walk)) return start; + } + start++; + } + + return NULL; +} + +fun StringLength (string) { + index = 0; + while (String(string).CharAt(index)) index++; + return index; +} + +fun StringCopy (source, beginning, end) { + local.destination = ""; + for (index = beginning; ( ( (end == NULL) || (index <= end) ) && (String(source).CharAt(index)) ); index++) { + local.destination += String(source).CharAt(index); + } + + return local.destination; +} + +fun StringReplace (source, pattern, replacement) { + local.found = StringString(source, pattern); + if (local.found == NULL) + return source; + + local.new_string = StringCopy (source, 0, local.found - 1) + + replacement + + StringCopy (source, local.found + StringLength(pattern), NULL); + + return local.new_string; +} + +# it makes sense to use it only for +# numbers up to 100 +fun StringToInteger (str) { + int = -1; + for (i=0; i<=100; i++) { + if (i+"" == str) { + int = i; + break; + } + } + return int; +} + +#----------------------------------------------------------------------------- +# Previous background colour +# #300a24 --> 0.19, 0.04, 0.14 +# New background colour +# #2c001e --> 0.16, 0.00, 0.12 +# +# New Lubuntu background +# 0.00, 0.09, 0.17 +Window.SetBackgroundTopColor (0.00, 0.09, 0.17); # Nice colour on top of the screen fading to +Window.SetBackgroundBottomColor (0.00, 0.09, 0.17); # an equally nice colour on the bottom + +logo.image = Image ("lubuntu_logo.png"); # "special://logo" is a special keyword which finds the logo image +logo.sprite = Sprite (); +logo.sprite.SetImage (logo.image); +logo.width = logo.image.GetWidth (); +logo.height = logo.image.GetHeight (); +logo.x = Window.GetX () + Window.GetWidth () / 2 - logo.width / 2; +logo.y = Window.GetY () + Window.GetHeight () / 2 - logo.height; +logo.z = 1000; +logo.sprite.SetX (logo.x); +logo.sprite.SetY (logo.y); +logo.sprite.SetZ (logo.z); +logo.sprite.SetOpacity (1); + +# Spacing below the logo - in pixels +logo_spacing = logo.height * 4; + +message_notification[0].image = ImageToTintedText (""); +message_notification[1].image = ImageToTintedText (""); +fsck_notification.image = ImageToActionText (""); + +status = "normal"; + +progress_indicator.bullet_off = Image ("progress_dot_off.png"); +progress_indicator.bullet_on = Image ("progress_dot_on.png"); +progress_indicator.bullet_width = progress_indicator.bullet_off.GetWidth (); +progress_indicator.bullet_height = progress_indicator.bullet_off.GetHeight (); +progress_indicator.bullet_hspacing = progress_indicator.bullet_width * 1.1; +progress_indicator.width = progress_indicator.bullet_width * 5; +progress_indicator.height = progress_indicator.bullet_height; +progress_indicator.y = logo.y + logo.height + (logo.height / 4); +progress_indicator.x = Window.GetX () + Window.GetWidth () / 2 - progress_indicator.width / 2; # logo.x + 26; + +# use a fixed string with ascending and descending stems to calibrate the +# bounding box for the first message, so the messages below don't move up +# and down according to *their* height. +first_line_height = ImageToTintedText ("AfpqtM").GetHeight(); + +# if the user has a 640x480 or 800x600 display, we can't quite fit everything +# (including passphrase prompts) with the target spacing, so scoot the text up +# a bit if needed. +top_of_the_text = TextYOffset(); + +#-----------------------------------------Logo functions------------------------------ + +# Call this when updating the screen +fun draw_logo () { + logo.sprite.SetX (logo.x); + logo.sprite.SetY (logo.y); + logo.sprite.SetZ (logo.z); + logo.sprite.SetOpacity (1); +} + + +#-----------------------------------------Progress Indicator-------------------------- +fun set_progress_indicator () { + + + # Here we assume that we can store half bullets on each half of the screen + # together with some spacing + local.x = progress_indicator.x; + + for (index = 0; index <= 4; index++) { + # Set the "off" bullets + progress_indicator.bullets_off[index].sprite = Sprite (progress_indicator.bullet_off); + progress_indicator.bullets_off[index].sprite.SetPosition (local.x, progress_indicator.y, 1000); + progress_indicator.bullets_off[index].x = local.x; + progress_indicator.bullets_off[index].y = progress_indicator.y; + progress_indicator.bullets_off[index].sprite.SetOpacity (1); + + #local.debug_medium_string = "Progress indicator " + index + ": x = " + progress_indicator.bullets_off[index].x + + # ", y = " + progress_indicator.bullets_off[index].y + ", logo width = " + logo.width + + # ", logo height = " + logo.height + " " + screen_width + " " + screen_height; + # + #(index % 2) && DebugMedium (local.debug_medium_string) || DebugBottom (local.debug_medium_string); + + # Set the "on" bullets on top of the "off" bullets and make them transparent + progress_indicator.bullets_on[index].sprite = Sprite (progress_indicator.bullet_on); + progress_indicator.bullets_on[index].x = progress_indicator.bullets_off[index].x; + progress_indicator.bullets_on[index].y = progress_indicator.bullets_off[index].y; + progress_indicator.bullets_on[index].sprite.SetPosition (progress_indicator.bullets_on[index].x, progress_indicator.bullets_on[index].y, 10000); + + progress_indicator.bullets_on[index].sprite.SetOpacity (0); + + local.x += progress_indicator.bullet_hspacing; + } + #local.debug_string = "Progress indicator: x1 = " + progress_indicator.x + ", x2 = " + local.x + ", y = " + progress_indicator.y + + # ", x logo = " + logo.x + ", y logo = " + logo.y + ", indicator width = " + progress_indicator.width; + #Debug(progress_indicator.bullets_off[0].x); +} + + +# We have 2 bullets, one on top of the other: +# The white one is on top of the red one and the former should +# slowly fade so as to get a nice transition effect. +fun switch_on_bullet (bullets_off, bullets_on, bullet_number, opacity) { + local.x = bullets_on[bullet_number].x; + local.y = bullets_on[bullet_number].y; + local.z = bullets_on[bullet_number].z; + + # Hide the bullets which are off + bullets_off[bullet_number].sprite.SetOpacity (0); + + # Show the bullets which are on + bullets_on[bullet_number].sprite.SetPosition (local.x, local.y, local.z); + bullets_on[bullet_number].sprite.SetOpacity (opacity); + + # Bump the number of times we have switched on bullets + global.times_bullets_switched++; +} + +fun switch_off_bullets () { + # Debug("Switching off progress indicator"); + + set_progress_indicator (); + global.times_bullets_switched = 0; + global.on_off = 1; +} + +# This is something that we can call when we exit +fun switch_on_bullets () { + # Debug("Switching off progress indicator"); + if (!global.progress_indicator.bullets_on) set_progress_indicator (); + local = global.progress_indicator; + + for (index = 0; bullets_on[index]; index++) { + switch_on_bullet (bullets_off, bullets_on, index, 1.0); + } +} + + +# Implement in boot progress callback +fun animate_progress_indicator (progress, time) { + if (global.progress_time == NULL) { + global.progress_time = progress; #time; + switch_off_bullets (); + } + +# Debug ("progress = " + progress + ", time = " + time + " times switched = " + global.times_bullets_switched + " on_off " + global.on_off); + +# if (global.times_bullets_switched == NULL) +# global.times_bullets_switched = 5; + +# if (global.on_off == NULL) +# global.on_off = 0; + + if ((progress - global.progress_time) >= 1.0) { + global.progress_time = progress; + + if (global.times_bullets_switched == 5) { + # Change which bullets are switched on + # and which ones are switched off + global.on_off = !global.on_off; + global.times_bullets_switched = 0; + } + + if (global.on_off) { + switch_on_bullet (progress_indicator.bullets_off, progress_indicator.bullets_on, + global.times_bullets_switched, 1.0); + } + else { + switch_on_bullet (progress_indicator.bullets_on, progress_indicator.bullets_off, + global.times_bullets_switched, 1.0); + } + } + + + # Start setting bullets to "on" with translucency +# for (index = 0; index <= 5; index++) { +# opacity = 0.0; +# while (opacity <= 1.0) { +# switch_on_bullet (progress_indicator.bullets_off, progress_indicator.bullets_on, +# index, opacity); +# opacity += 0.1; +# } +# } +} + + +#-----------------------------------------Label utility functions--------------------- + +# label should be either a string or NULL +# Images for n lines will be created and returned as items of the +# message_label array +# +fun get_message_label (label, is_fake, is_action_line) { + # Debug("Get Label position"); + local.message_label; + + if (is_fake) + # Create a fake label so as to get the y coordinate of + # a standard-length label. + local.message_image = ImageToTintedText ("This is a fake message"); + else + local.message_image = (is_action_line) && ImageToActionText (label) || ImageToTintedText (label); + + message_label.width = message_image.GetWidth (); + message_label.height = message_image.GetHeight (); + + # Center the line horizontally + message_label.x = Window.GetX () + Window.GetWidth () / 2 - message_label.width / 2; + + message_label.y = top_of_the_text; + + # Put the 2nd line below the fsck line + if (is_action_line) { + local.fsck_label.y = message_label.y + (first_line_height + first_line_height / 2); + message_label.y = local.fsck_label.y + (first_line_height * 2); + } + + # Debug("action label x = " + message_label.x + " y = " + message_label.y ); + +# message_debug = "msg_x = " + message_label.x + " msg_y = " + message_label.y + +# "msg_width = " + message_label.width + " msg_height = " + +# message_label.height + " message = " + label; +# Debug(message_debug); + + return message_label; + +} + +# Create an fsck label and/or get its position +fun get_fsck_label (label, is_fake) { + # Debug("Get Label position"); + local.fsck_label = global.progress_label; + + if (is_fake) + fsck_label.image = ImageToTintedText ("This is a fake message"); + else + fsck_label.image = ImageToTintedText (label); + + fsck_label.width = fsck_label.image.GetWidth (); + fsck_label.height = fsck_label.image.GetHeight (); + + # Centre the label horizontally + fsck_label.x = Window.GetX () + Window.GetWidth () / 2 - fsck_label.width / 2; + + local.first_label = get_message_label (label, 1, 0); + + # Place the label below the 1st message line + fsck_label.y = local.first_label.y + local.first_label.height + (local.first_label.height / 2); + +# message_debug = "msg_x = " + fsck_label.x + " msg_y = " + fsck_label.y + +# "msg_width = " + fsck_label.width + " msg_height = " + +# fsck_label.height + " message = " + label; +# Debug(message_debug); + + return fsck_label; +} + +#-----------------------------------------Message stuff -------------------------------- +# + +# Set up a message label +# +# NOTE: this is called when doing something like 'plymouth message "hello world"' +# +fun setup_message (message_text, x, y, z, index) { + # Debug("Message setup"); + global.message_notification[index].image = (index) && ImageToActionText (message_text) || ImageToTintedText (message_text); + + # Set up the text message, if any + message_notification[index].x = x; + message_notification[index].y = y; + message_notification[index].z = z; + + message_notification[index].sprite = Sprite (); + message_notification[index].sprite.SetImage (message_notification[index].image); + message_notification[index].sprite.SetX (message_notification[index].x); + message_notification[index].sprite.SetY (message_notification[index].y); + message_notification[index].sprite.SetZ (message_notification[index].z); + +} + +fun show_message (index) { + if (global.message_notification[index].sprite) global.message_notification[index].sprite.SetOpacity(1); +} + +fun hide_message (index) { + if (global.message_notification[index].sprite) global.message_notification[index].sprite.SetOpacity(0); +} + + + + +# the callback function is called when new message should be displayed. +# First arg is message to display. +fun message_callback (message) +{ + # Debug("Message callback"); + is_fake = 0; + if (!message || (message == "")) is_fake = 1; + + local.substring = "keys:"; + + # Look for the "keys:" prefix + local.keys = StringString(message, local.substring); + + local.is_action_line = (keys != NULL); + #Debug("keys " + local.keys + " substring length = " + StringLength(local.substring)); + + # Get the message without the "keys:" prefix + if (keys != NULL) + message = StringCopy (message, keys + StringLength(local.substring), NULL); + + # Get the message without the "fsckd-cancel-msg" prefix as we don't support i18n + substring = "fsckd-cancel-msg:"; + keys = StringString(message, substring); + if (keys != NULL) + message = StringCopy(message, keys + StringLength(substring), NULL); + + + local.label.is_fake = is_fake; + label = get_message_label(message, is_fake, is_action_line); + label.z = 10000; + + setup_message (message, label.x, label.y, label.z, is_action_line); + if (prompt_active && local.is_action_line) + hide_message (is_action_line); + else + show_message (is_action_line); + +} + + +#-----------------------------------------Display Password stuff ----------------------- +# + +fun password_dialogue_setup (message_label) { + # Debug("Password dialog setup"); + + local.entry; + local.bullet_image; + + bullet_image = Image ("progress_dot_off.png"); + entry.image = Image ("password_field.png"); + + # Hide the normal labels + prompt_active = 1; + if (message_notification[1].sprite) hide_message (1); + + # Set the prompt label + label = get_message_label(message_label, 0, 1); + label.z = 10000; + + setup_message (message_label, label.x, label.y, label.z, 2); + show_message (2); + + # Set up the text entry which contains the bullets + entry.sprite = Sprite (); + entry.sprite.SetImage (entry.image); + + # Centre the box horizontally + entry.x = Window.GetX () + Window.GetWidth () / 2 - entry.image.GetWidth () / 2; + + # Put the entry below the second label. + entry.y = message_notification[2].y + label.height; + + #Debug ("entry x = " + entry.x + ", y = " + entry.y); + entry.z = 10000; + entry.sprite.SetX (entry.x); + entry.sprite.SetY (entry.y); + entry.sprite.SetZ (entry.z); + + global.password_dialogue = local; +} + +fun password_dialogue_opacity (opacity) { + # Debug("Password dialog opacity"); + global.password_dialogue.opacity = opacity; + local = global.password_dialogue; + + # You can make the box translucent with a float + # entry.sprite.SetOpacity (0.3); + entry.sprite.SetOpacity (opacity); + label.sprite.SetOpacity (opacity); + + if (bullets) { + for (index = 0; bullets[index]; index++) { + bullets[index].sprite.SetOpacity (opacity); + } + } +} + + +# The callback function is called when the display should display a password dialogue. +# First arg is prompt string, the second is the number of bullets. +fun display_password_callback (prompt, bullets) { + # Debug("Password dialog setup"); + + global.status = "password"; + if (!global.password_dialogue) password_dialogue_setup(prompt); + password_dialogue_opacity (1); + bullet_width = password_dialogue.bullet_image.GetWidth(); + bullet_y = password_dialogue.entry.y + + password_dialogue.entry.image.GetHeight () / 2 - + password_dialogue.bullet_image.GetHeight () / 2; + margin = bullet_width; + spaces = Math.Int( (password_dialogue.entry.image.GetWidth () - (margin * 2)) / (bullet_width / 2 ) ); + #Debug ("spaces = " + spaces + ", bullets = " + bullets); + bullets_area.width = margin + spaces * (bullet_width / 2); + bullets_area.x = Window.GetX () + Window.GetWidth () / 2 - bullets_area.width / 2; + #DebugBottom ("pwd_entry x = " + password_dialogue.entry.x + ", bullets_area.x = " + bullets_area.x + ", bullets_area.width = " + bullets_area.width); + if (bullets > spaces) + bullets = spaces; + for (index = 0; password_dialogue.bullets[index] || index < bullets; index++){ + if (!password_dialogue.bullets[index]) { + password_dialogue.bullets[index].sprite = Sprite (); + password_dialogue.bullets[index].sprite.SetImage (password_dialogue.bullet_image); + password_dialogue.bullets[index].x = bullets_area.x + # password_dialogue.entry.x + margin + + index * bullet_width / 2; + password_dialogue.bullets[index].sprite.SetX (password_dialogue.bullets[index].x); + password_dialogue.bullets[index].y = bullet_y; + password_dialogue.bullets[index].sprite.SetY (password_dialogue.bullets[index].y); + password_dialogue.bullets[index].z = password_dialogue.entry.z + 1; + password_dialogue.bullets[index].sprite.SetZ (password_dialogue.bullets[index].z); + } + + password_dialogue.bullets[index].sprite.SetOpacity (0); + + if (index < bullets) { + password_dialogue.bullets[index].sprite.SetOpacity (1); + } + } +} + +Plymouth.SetDisplayPasswordFunction (display_password_callback); + +Plymouth.SetMessageFunction (message_callback); + +Plymouth.SetBootProgressFunction (animate_progress_indicator); + +# Plymouth.SetBootProgressFunction: the callback function is called with two numbers, the progress (between 0 and 1) and the time spent booting so far +# Plymouth.SetRootMountedFunction: the callback function is called when a new root is mounted +# Plymouth.SetKeyboardInputFunction: the callback function is called with a string containing a new character entered on the keyboard + +#----------------------------------------- FSCK Counter -------------------------------- + +# Initialise the counter +fun init_fsck_count () { + # The number of fsck checks in this cycle + global.counter.total = 0; + # The number of fsck checks already performed + the current one + global.counter.current = 1; + # The previous fsck + global.counter.last = 0; +} + +# Increase the total counter +fun increase_fsck_count () { + global.counter.total++; +} + +fun increase_current_fsck_count () { + global.counter.last = global.counter.current++; +} + +# Clear the counter +fun clear_fsck_count () { + global.counter = NULL; + init_fsck_count (); +} + +#----------------------------------------- Progress Label ------------------------------ + + +# Change the opacity level of a progress label +# +# opacity = 1 -> show +# opacity = 0 -> hide +# opacity = 0.3 (or any other float) -> translucent +# +fun set_progress_label_opacity (opacity) { + # the label + progress_label.sprite.SetOpacity (opacity); + + # Make the slot available again when hiding the bar + # So that another bar can take its place + if (opacity == 0) { + progress_label.is_available = 1; + progress_label.device = ""; + } +} + +# Set up a new Progress Bar +# +# TODO: Make it possible to reuse (rather than recreate) a bar +# if .is_available = 1. Ideally this would just reset the +# label, the associated +# device and the image size of the sprite. + +fun init_progress_label (device, status_string) { + # Make the slot unavailable + global.progress_label.is_available = 0; + progress_label.progress = 0; + progress_label.device = device; + progress_label.status_string = status_string; +} + +# See if the progress label is keeping track of the fsck +# of "device" +# +fun device_has_progress_label (device) { + #DebugBottom ("label device = " + progress_label.device + " checking device " + device); + return (progress_label.device == device); +} + +# Update the Progress bar which corresponds to index +# +fun update_progress_label (progress) { + # If progress is NULL then we just refresh the label. + # This happens when only counter.total has changed. + if (progress != NULL) { + progress_label.progress = progress; + + #Debug("device " + progress_label.device + " progress " + progress); + + # If progress >= 100% hide the label and make it available again + if (progress >= 100) { + set_progress_label_opacity (0); + + # See if we any other fsck check is complete + # and, if so, hide the progress bars and the labels + on_fsck_completed (); + + return 0; + } + } + # Update progress label here + # + # FIXME: the queue logic from this theme should really be moved into mountall + # instead of using string replacement to deal with localised strings. + label = StringReplace (progress_label.status_string[0], "%1$d", global.counter.current); + label = StringReplace (label, "%2$d", global.counter.total); + label = StringReplace (label, "%3$d", progress_label.progress); + label = StringReplace (label, "%%", "%"); + + progress_label = get_fsck_label (label, 0); + #progress_label.progress = progress; + + progress_label.sprite = Sprite (progress_label.image); + + # Set up the bar + progress_label.sprite.SetPosition(progress_label.x, progress_label.y, 1); + + set_progress_label_opacity (1); + +} + +# Refresh the label so as to update counters +fun refresh_progress_label () { + update_progress_label (NULL); +} + +#----------------------------------------- FSCK Queue ---------------------------------- + +# Initialise the fsck queue +fun init_queue () { + global.fsck_queue[0].device; + global.fsck_queue[0].progress; + global.fsck_queue.counter = 0; + global.fsck_queue.biggest_item = 0; +} + +fun clear_queue () { + global.fsck_queue = NULL; + init_queue (); +} + +# Return either the device index in the queue or -1 +fun queue_look_up_by_device (device) { + for (i=0; i <= fsck_queue.biggest_item; i++) { + if ((fsck_queue[i]) && (fsck_queue[i].device == device)) + return i; + } + return -1; +} + +# Keep track of an fsck process in the queue +fun add_fsck_to_queue (device, progress) { + # Look for an empty slot in the queue + for (i=0; global.fsck_queue[i].device; i++) { + continue; + } + local.index = i; + + # Set device and progress + global.fsck_queue[local.index].device = device; + global.fsck_queue[local.index].progress = progress; + + # Increase the queue counter + global.fsck_queue.counter++; + + # Update the max index of the array for iterations + if (local.index > global.fsck_queue.biggest_item) + global.fsck_queue.biggest_item = local.index; + + #DebugMedium ("Adding " + device + " at " + local.index); +} + +fun is_queue_empty () { + return (fsck_queue.counter == 0); +} + +fun is_progress_label_available () { + return (progress_label.is_available == 1); +} + + +# This should cover the case in which the fsck checks in +# the queue are completed before the ones showed in the +# progress label +fun on_queued_fsck_completed () { + if (!is_queue_empty ()) + return; + + # Hide the extra label, if any + #if (progress_bar.extra_label.sprite) + # progress_bar.extra_label.sprite.SetOpacity(0); +} + +fun remove_fsck_from_queue (index) { + # Free memory which was previously allocated for + # device and progress + global.fsck_queue[index].device = NULL; + global.fsck_queue[index].progress = NULL; + + # Decrease the queue counter + global.fsck_queue.counter--; + + # See if there are other processes in the queue + # if not, clear the extra_label + on_queued_fsck_completed (); +} + +fun on_fsck_completed () { + # We have moved on to tracking the next fsck + increase_current_fsck_count (); + + if (!is_progress_label_available ()) + return; + + if (!is_queue_empty ()) + return; + + # Hide the progress label + if (progress_label.sprite) + progress_label.sprite.SetOpacity (0); + + # Clear the queue + clear_queue (); + + # Clear the fsck counter + clear_fsck_count (); +} + +# Update an fsck process that we keep track of in the queue +fun update_progress_in_queue (index, device, progress) { + # If the fsck is complete, remove it from the queue + if (progress >= 100) { + remove_fsck_from_queue (index); + on_queued_fsck_completed (); + return; + } + + global.fsck_queue[index].device = device; + global.fsck_queue[index].progress = progress; + +} + +# TODO: Move it to some function +# Create an empty queue +#init_queue (); + + +#----------------------------------------- FSCK Functions ------------------------------ + + +# Either add a new bar for fsck checks or update an existing bar +# +# NOTE: no more than "progress_bar.max_number" bars are allowed +# +fun fsck_check (device, progress, status_string) { + + # The 1st time this will take place + if (!global.progress_label) { + # Increase the fsck counter + increase_fsck_count (); + + # Set up a new label for the check + init_progress_label (device, status_string); + update_progress_label (progress); + + return; + } + + + if (device_has_progress_label (device)) { + # Update the progress of the existing label + update_progress_label (progress); + } + else { + # See if there's already a slot in the queue for the device + local.queue_device_index = queue_look_up_by_device(device); + + # See if the progress_label is available + if (progress_label.is_available) { + +# local.my_string = "available index " + local.available_index + " progress_bar counter is " + progress_bar.counter; +# Debug(local.my_string); + + + # If the fsck check for the device was in the queue, then + # remove it from the queue + if (local.queue_device_index >= 0) { + remove_fsck_from_queue (index); + } + else { + # Increase the fsck counter + increase_fsck_count (); + } + +# local.my_string += local.message; + #Debug("setting new label for device " + device + " progress " + progress); + + # Set up a new label for the check + init_progress_label (device, status_string); + update_progress_label (progress); + + } + # If the progress_label is not available + else { + + # If the fsck check for the device is already in the queue + # just update its progress in the queue + if (local.queue_device_index >= 0) { + #DebugMedium("Updating queue at " + local.queue_device_index + " for device " + device); + update_progress_in_queue (local.queue_device_index, device, progress); + } + # Otherwise add the check to the queue + else { + #DebugMedium("Adding device " + device + " to queue at " + local.queue_device_index); + add_fsck_to_queue (device, progress); + + # Increase the fsck counter + increase_fsck_count (); + + refresh_progress_label (); + } + + } + } + +# if (!is_queue_empty ()) { +# DebugBottom("Extra label for "+ device); + #} +# else { +# DebugBottom("No extra label for " + device + ". 1st Device in the queue "+ fsck_queue[0].device + " counter = " + global.fsck_queue.counter); +# } +} + + +#-----------------------------------------Update Status stuff -------------------------- +# +# The update_status_callback is what we can use to pass plymouth whatever we want so +# as to make use of features which are available only in this program (as opposed to +# being available for any theme for the script plugin). +# +# Example: +# +# Thanks to the current implementation, some scripts can call "plymouth --update=fsck:sda1:40" +# and this program will know that 1) we're performing and fsck check, 2) we're checking sda1, +# 3) the program should set the label progress to 40% +# +# Other features can be easily added by parsing the string that we pass plymouth with "--update" +# +fun update_status_callback (status) { +# Debug(status); + if (!status) return; + + string_it = 0; + update_strings[string_it] = ""; + + for (i=0; (String(status).CharAt(i) != ""); i++) { + local.temp_char = String(status).CharAt(i); + if (temp_char != ":") + update_strings[string_it] += temp_char; + else + update_strings[++string_it] = ""; + } + +# my_string = update_strings[0] + " " + update_strings[1] + " " + update_strings[2]; +# Debug(my_string); + # Let's assume that we're dealing with these strings fsck:sda1:40 + if ((string_it >= 2) && (update_strings[0] == "fsck")) { + + device = update_strings[1]; + progress = update_strings[2]; + status_string[0] = update_strings[3]; # "Checking disk %1$d of %2$d (%3$d %% complete)" + if (!status_string[0]) + status_string[0] = "Checking disk %1$d of %2$d (%3$d %% complete)"; + + if ((device != "") && (progress != "")) { + progress = StringToInteger (progress); + + # Make sure that the fsck_queue is initialised + if (!global.fsck_queue) + init_queue (); + + # Make sure that the fsck counter is initialised + if (!global.counter) + init_fsck_count (); + +# if (!global.progress_bar.extra_label.sprite) +# create_extra_fsck_label (); + + # Keep track of the fsck check + fsck_check (device, progress, status_string); + } + + } + + # systemd-fsckd pass fsckd::: + if (update_strings[0] == "fsckd") { + number_devices = StringToInteger(update_strings[1]); + + if (number_devices > 0) { + label = update_strings[3]; + + progress_label = get_fsck_label (label, 0); + progress_label.sprite = Sprite (progress_label.image); + progress_label.sprite.SetPosition(progress_label.x, progress_label.y, 1); + progress_label.sprite.SetOpacity (1); + } else { + if (progress_label.sprite) + progress_label.sprite.SetOpacity (0); + } + } + +} +Plymouth.SetUpdateStatusFunction (update_status_callback); + +#-----------------------------------------Display Question stuff ----------------------- +# +# TODO: Implement this if needed +# +# The callback function is called when the display should display a question dialogue. +# First arg is prompt string, the second is the entry contents. +#fun display_question_callback (prompt_string, entry_contents) +#{ +# time++; +#} +# +#Plymouth.SetDisplayQuestionFunction (display_question_callback); + +#-----------------------------------------Refresh stuff -------------------------------- +# +# Calling Plymouth.SetRefreshFunction with a function will set that function to be +# called up to 50 times every second, e.g. +# +# NOTE: if a refresh function is not set, Plymouth doesn't seem to be able to update +# the screen correctly +# +fun refresh_callback () +{ + draw_logo (); +} +Plymouth.SetRefreshFunction (refresh_callback); + + +#-----------------------------------------Display Normal stuff ----------------------- +# +# The callback function is called when the display should return to normal +fun display_normal_callback () +{ + global.status = "normal"; + if (global.password_dialogue) { + password_dialogue_opacity (0); + global.password_dialogue = NULL; + if (message_notification[2].sprite) hide_message(2); + prompt_active = 0; + } + + if (message_notification[1].sprite) show_message (1); + + +} + +Plymouth.SetDisplayNormalFunction (display_normal_callback); + + +#----------------------------------------- Quit -------------------------------- + +# TODO: Maybe we should also hide any other dialog +# Show the logo and make the progress indicator look full when on exit +fun quit_callback () +{ + logo.sprite.SetOpacity (1); + switch_on_bullets (); +} + +Plymouth.SetQuitFunction(quit_callback); diff --git a/src/usr/share/plymouth/themes/lubuntu-next-logo/lubuntu_logo.png b/src/usr/share/plymouth/themes/lubuntu-next-logo/lubuntu_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..3271032d10454c55e93bc0a6bfbe55771c445027 GIT binary patch literal 22106 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 V2T_eI5X}Gp002ovPDHLkV1l89mrMWv literal 0 HcmV?d00001 diff --git a/src/usr/share/plymouth/themes/lubuntu-next-logo/password_field.png b/src/usr/share/plymouth/themes/lubuntu-next-logo/password_field.png new file mode 100644 index 0000000000000000000000000000000000000000..d40aaf24febc7c1f8d490783f82ba92d5b7c98e8 GIT binary patch literal 1078 zcmV-61j+k}P)+DIfNwJln( zqNP-l&31R)ot>R|zPJ3|`ns)xH=X@{;KvS=KZket^5ze$?>)_z&p?iSIR;Q{%9&g~ zJ?Ha@KzJj8DCsH7l;m9SjK2Z!{||-dgd&saJI8K7h5@j_Fp-$h6FC#g`G6e=y!7!0H zpl9~T%5v?*##w3WcHFHlxWQ8UB&E&@4dT!;o{{_JDu)PrBs#W1jeHq{Vbc`Z#0kpbnkwA z=B<<0zuo-4BKI|h_cls$X=3hM)bz}~bNbEf>?fanWP-sO0^_OO?z~d1RIVji+HO2< zChmO=`$VK9mniYti>tk6>yHhV4H$1f|MFmcX>q~a!ib~~g9jn@1uh$naBgNg%kzAS zB?HD=H%VqHacufvpPmjLoVYJ+*?0iJfKtkR;WKcf;qE#Bz#G|6*kfJ|2k`fDmHQ*% z;0;dz{|?arKr{dl4FE&~0MP(IGys5T03aFwhz0?3-ZdX?=a=oA~_4?|)D(mmjihz<4^iF!$x!`U`jN|N6jmp(D{4 z>^6p9A1?t3_8X7Ok9PNJ&mUgeZZw)vzn@Jjr6yTMU>q(T&5@+3=@w?PgTzWJ+ntvmTM0V-1}q(-7J*FFNTYdQjprj78efno(Wc=nXq%_jqmMr|;#wl*2}xaM2X z$^5`V6PUeo1yhQ^R-5^WoV-=85l+|o^9mb=;c*v-@)fM`=b+5+qjgcu%CFwHF!ZH@y{;yO8E-=iBA%ricoe#TG*szS0|vdKgk_2BafESITyPWo);EbL z7f?i1deCeD`9PL(p-Pf(o686Csd@=T!9}M{P)6hRP$zwVt~S+YdnSs@_;=Y%AiI^haL+`|_l zB;f|6a{(krL=eq{2s1z3B(KohVJxsES}OH)clE1!^{OY;n=4{80LRnt8o0~ayaUGM zZuxi`F6BlBM!Avd%@xPfum=wE9{aAH8Z8dMD}8?krZZq4zYmV5A@yBb0aANgvtahX z1dLf(TmpmUoDFL4{0i_Lc&6)RJCKMZU~Sc{@7fyVvSJ1MtZ<`3O~8`c+nSWih3t#6 zo`9FYy=>0Pr|9q=xCWjBFSMS3;%RuuA6q$}l{bdS5*0YpL5JC@fT5MXYikh8z)l3S zr8ENzP`1hy5>1wGX|xEm1Mpss@8q-qOCU!026$td2P%yv9yxu|-mJYEjsZXG_FKy_ z$ea~@(xrLDx&XM8ixm1P8>2LR*Ve}=v*qYM8!zh&tqMZ#mp7jUd8}L>0NBbc)b|(ev%zqgEE_Y9) zzZm_}cWr4_Z4w>qjr$m(>RPDc@B67O^+)snW8?qz0sI0*^6MR+As+Go0000