Warning, /plasma/breeze-plymouth/breeze/breeze.script.cmake is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2012-2016 Harald Sitter <sitter@kde.org>
0003     SPDX-FileCopyrightText: 2009 Canonical Ltd. 
0004     SPDX-FileContributor:  Alberto Milone <alberto.milone@canonical.com>
0005     Based on the example provided with the "script plugin" written by:
0006     SPDX-FileContributor: Charlie Brej   <cbrej@cs.man.ac.uk>
0007 
0008     SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0009 */
0010 
0011 /**
0012  * 16bit debug override. Comment out to see what everything looks like on a 16bit
0013  * framebuffer/output.
0014  */
0015 // Window.GetBitsPerPixel = fun() { return 4; };
0016 
0017 // -------------------------------- Assets ---------------------------------- //
0018 
0019 global.title.text = "@DISTRO_NAME@ @DISTRO_VERSION@";
0020 global.defaults.font.default = "Noto Sans 12";
0021 global.defaults.font.title = "Noto Sans 14";
0022 
0023 global.assets = [];
0024 if (Window.GetBitsPerPixel() == 4) {
0025     assets.logo          = "images/16bit/@DISTRO_LOGO@.logo.png";
0026     assets.text_input    = "images/16bit/text-input.png";
0027 
0028     assets.spinner_base  = "images/16bit/spinner";
0029 } else {
0030     assets.logo          = "images/@DISTRO_LOGO@.logo.png";
0031     assets.text_input    = "images/text-input.png";
0032 
0033     assets.spinner_base  = "images/spinner";
0034 }
0035 
0036 // -------------------------------- Colors ---------------------------------- //
0037 /**
0038  * General purpuse Color container to hold red, green, blue as any value
0039  * (real advisable).
0040  */
0041 Color = fun(red, green, blue) {
0042     local.color = [];
0043     color.red = red;
0044     color.green = green;
0045     color.blue = blue;
0046     return color | global.Color;
0047 } | [];
0048 
0049 global.colors = [];
0050 colors.black = Color(0, 0, 0);
0051 colors.icon_blue = Color(0.1137, 0.6000, 0.9529);
0052 colors.plasma_blue = Color(0.2392, 0.6824, 0.913);
0053 colors.paper_white = Color(0.9882, 0.9882, 0.9882);
0054 colors.charcoal_grey = Color(0.1922, 0.2118, 0.2314);
0055 colors.cardboard_grey = Color(0.9373, 0.9412, 0.9451);
0056 
0057 colors.neon_blue = Color(0.1608, 0.5020, 0.7255);
0058 colors.neon_green = Color(0.1020, 0.7373, 0.6118);
0059 
0060 global.palette = [];
0061 palette.background.top = colors.@BACKGROUND_TOP_COLOR@;
0062 palette.background.bottom = colors.@BACKGROUND_BOTTOM_COLOR@;
0063 palette.text.normal = colors.cardboard_grey;
0064 palette.text.tinted = colors.cardboard_grey;
0065 palette.text.action = colors.cardboard_grey;
0066 palette.text.contrast = colors.charcoal_grey; // Inverse essentially
0067 
0068 /**
0069  * Helper overload to apply background colors from global.palette to the Window
0070  */
0071 Window.ApplyBackgroundColors = fun() {
0072     Window.SetBackgroundTopColor(palette.background.top.red,
0073                                  palette.background.top.green,
0074                                  palette.background.top.blue);
0075     if (Window.GetBitsPerPixel() == 4) { // Force no gradient on 16bit.
0076         Window.SetBackgroundBottomColor(palette.background.top.red,
0077                                      palette.background.top.green,
0078                                      palette.background.top.blue);
0079     } else {
0080         Window.SetBackgroundBottomColor(palette.background.bottom.red,
0081                                         palette.background.bottom.green,
0082                                         palette.background.bottom.blue);
0083     }
0084 };
0085 
0086 // ------------------------------- Classes ---------------------------------- //
0087 
0088 /**
0089  * class SpriteImage : Sprite {
0090  *     Image image,   # Image instance created by and for the Sprite
0091  *     int width,     # Image width
0092  *     int height,    # Image height
0093  *  };
0094  */
0095 
0096 /**
0097  * General purpose sprite-image combination.
0098  * The type itself is a Sprite that has an image property through which the image
0099  * powering the sprite may be accessed.
0100  * Members of the sprite are only updated on initial creation, any future changes
0101  * to the actually used image need to be reflected manually
0102  */
0103 SpriteImage = fun(asset) {
0104     local.sprite = Sprite();
0105     sprite.image = Image(asset);
0106     sprite.width = sprite.image.GetWidth();
0107     sprite.height = sprite.image.GetHeight();
0108     sprite.SetImage(sprite.image);
0109     return sprite | global.SpriteImage;
0110 } | Sprite;
0111 
0112 SpriteImage.SetSpriteImage = fun(image) {
0113     this.image = image;
0114     this.width = image.GetWidth();
0115     this.height = image.GetHeight();
0116     this.SetImage(this.image);
0117 };
0118 
0119 // --------------------------------- Debug ---------------------------------- //
0120 // TODO: it may be handy to move all debug methods into a separate file
0121 //   and configure_file them into the master script iff explicitly enabled.
0122 //   This would reduce the script size and possibly eval time. Although
0123 //   in the grand scheme of things I am not sure this script takes up a lot of
0124 //   resources to begin with.
0125 debugsprite = Sprite();
0126 debugsprite_bottom = Sprite();
0127 debugsprite_medium = Sprite();
0128 
0129 // are we currently prompting for a password?
0130 prompt_active = 0;
0131 
0132 /**
0133  * General purpose function to create an image from a string.
0134  * \param text the string to print
0135  * \param color the color the string should use in the image
0136  * \returns Image containg the text
0137  */
0138 fun WriteText(text, color, font) {
0139   if (!color) {
0140     color = palette.text.normal;
0141   }
0142   if (!font) {
0143     font = defaults.font.default;
0144   }
0145   return Image.Text(text, color.red, color.green, color.blue, 1,  font);
0146 }
0147 
0148 /** Create regular text image. \see WriteText */
0149 fun ImageToText (text, font) {
0150     return WriteText(text, color, font);
0151 }
0152 
0153 String.ToImage = fun(color, font) {
0154   return WriteText(this, color, font);
0155 };
0156 
0157 /** Create tinted text image. \see WriteText */
0158 fun ImageToTintedText (text) {
0159     return WriteText(text, palette.text.tinted);
0160 }
0161 
0162 /** Create action text image. \see WriteText */
0163 fun ImageToActionText (text) {
0164     return WriteText(text, palette.text.action);
0165 }
0166 
0167 fun Debug(text) {
0168     debugsprite.SetImage(ImageToText (text));
0169     debugsprite.SetPosition(0, 0, 1);
0170 }
0171 
0172 fun DebugBottom(text) {
0173     debugsprite_bottom.SetImage(ImageToText(text));
0174     debugsprite_bottom.SetPosition(0, (Window.GetHeight (0) - 20), 1);
0175 }
0176 
0177 fun DebugMedium(text) {
0178     debugsprite_medium.SetImage(ImageToText (text));
0179     debugsprite_medium.SetPosition(0, (Window.GetHeight (0) - 60), 1);
0180 }
0181 
0182 /**
0183  * Debug helper to simulate something like a log on the right hand side of
0184  * the display. There is a global ActionStack which gets .Log("foo")'d into
0185  * which essentially prepends the string to an internal string buffer which
0186  * is then rendered into a sprite.
0187  * The buffer is not ever emptied so this basically is growing memory
0188  * consumption. And it's offset placing from the rigth side is also getting
0189  * increasingly wrong as the largest ever logged line dictates the offset.
0190  */
0191 Logger = fun() {
0192     local.logger = [];
0193     local.logger.log = "";
0194     local.logger.sprite = Sprite();
0195     return logger | global.Logger;
0196 } | [];
0197 
0198 Logger.Log = fun(text) {
0199     log = text + "\n" + log;
0200     Print();
0201 };
0202 
0203 Logger.Print = fun() {
0204     sprite.SetImage(ImageToText(log));
0205     sprite.SetPosition(Window.GetMaxWidth() - sprite.GetImage().GetWidth() - 16, 0, 1);
0206 };
0207 global.logger = Logger();
0208 
0209 /**
0210  * Calulates the Y of the label "box". That is, the top most point at which
0211  * we should put elements that are meant to go below the logo/spinner/whatevs
0212  * as to not overlap with the aforementioned. This includes message display,
0213  * password prompt and so forth.
0214  */
0215 fun TextYOffset() {
0216     // Put the 1st line below the logo.
0217     local.y = spin.GetY() + spin.GetHeight();
0218     local.text_height = first_line_height * 7.5;
0219     // The maximum Y we may end at, if we exceed this we'll try to scoot up
0220     // a bit. This includes the Window offset itself as we position ourselves
0221     // relative to the Spinner which is relative to the Logo which is relative
0222     // to the center of the window TAKING INTO ACCOUNT the y offset of the
0223     // window!
0224     local.max_y = Window.GetHeight() + Window.GetY();
0225 
0226     if (y + text_height > max_y) {
0227         y = max_y - text_height;
0228     } else {
0229         y = y + ((max_y - y - text_height) / 2);
0230     }
0231 
0232     // This basically undoes whatever went on above, to a degree...
0233     // If the y would overlap with the Spinner (bottom most element of the
0234     // static cruft) we move it further down so that at least half a line of
0235     // space is between the spinner and our y.
0236     if (y < spin.GetY() + spin.GetHeight() + first_line_height / 2) {
0237         y = spin.GetY() + spin.GetHeight() + first_line_height / 2;
0238     }
0239 
0240     return y;
0241 }
0242 
0243 Window.GetMaxWidth = fun() {
0244     width = 0;
0245     for (i = 0; Window.GetWidth(i); i++) {
0246         width = Math.Max(width, Window.GetWidth(i));
0247     }
0248     return width;
0249 };
0250 
0251 Window.GetMaxHeight = fun() {
0252     height = 0;
0253     for (i = 0; Window.GetHeight(i); i++) {
0254         height = Math.Max(height, Window.GetHeight(i));
0255     }
0256     return height;
0257 };
0258 
0259 // --------------------------------- String --------------------------------- //
0260 # This is the equivalent for strstr()
0261 fun StringString(string, substring) {
0262     start = 0;
0263     while (String(string).CharAt (start)) {
0264         walk = 0;
0265         while (String(substring).CharAt (walk) == String(string).CharAt (start + walk) ) {
0266             walk++;
0267             if (!String(substring).CharAt (walk)) return start;
0268         }
0269         start++;
0270     }
0271 
0272     return NULL;
0273 }
0274 
0275 fun StringLength (string) {
0276     index = 0;
0277     while (String(string).CharAt(index))
0278         index++;
0279     return index;
0280 }
0281 
0282 // String.Length = fun(string) {
0283 //     index = 0;
0284 //     while (String(string).CharAt(index))
0285 //         index++;
0286 //     return index;
0287 // };
0288 
0289 fun StringCopy (source, beginning, end) {
0290     local.destination = "";
0291     for (index = beginning;
0292          (((end == NULL) || (index <= end) ) && (String(source).CharAt(index)));
0293          index++) {
0294         local.destination += String(source).CharAt(index);
0295     }
0296 
0297     return local.destination;
0298 }
0299 
0300 fun StringReplace (source, pattern, replacement) {
0301     local.found = StringString(source, pattern);
0302     if (local.found == NULL) {
0303         return source;
0304     }
0305 
0306     local.new_string = StringCopy (source, 0, local.found - 1) +
0307                        replacement +
0308                        StringCopy (source, local.found + StringLength(pattern), NULL);
0309 
0310     return local.new_string;
0311 }
0312 
0313 # it makes sense to use it only for
0314 # numbers up to 100
0315 fun StringToInteger (str) {
0316     int = -1;
0317     for (i=0; i<=100; i++) {
0318         if (i+"" == str) {
0319             int = i;
0320             break;
0321         }
0322     }
0323     return int;
0324 }
0325 
0326 // ------------------------------ Background -------------------------------- //
0327 Window.ApplyBackgroundColors();
0328 global.backgroundApplied = false;
0329 
0330 // --------------------------------- Logo ----------------------------------- //
0331 
0332 Logo = fun() {
0333     local.logo = SpriteImage(assets.logo);
0334     logo.x = Window.GetX() + Window.GetWidth() / 2 - logo.width / 2;
0335     logo.y = Window.GetY() + Window.GetHeight() / 2 - logo.height / 2;
0336     logo.z = 1000;
0337     logo.SetPosition(logo.x, logo.y, logo.z);
0338 
0339     logo.name = Sprite(title.text.ToImage(NULL, defaults.font.title));
0340     logo.name.x = Window.GetX() + Window.GetWidth() / 2 - logo.name.GetImage().GetWidth() / 2;
0341     logo.name.y = logo.y + logo.height + logo.name.GetImage().GetHeight() / 2;
0342     logo.name.z = logo.z;
0343     logo.name.SetPosition(logo.name.x, logo.name.y, logo.z);
0344 
0345     logo.height = logo.height + logo.name.GetImage().GetHeight() ;
0346 
0347     return logo | global.Logo;
0348 } | SpriteImage;
0349 
0350 Logo.SetOpacity_ = fun(o) {
0351     o = Math.Clamp(o, 0.0, 1.0);
0352     this.SetOpacity(o);
0353     this.name.SetOpacity(o);
0354 };
0355 
0356 logo = Logo();
0357 logo.SetOpacity_(0);
0358 
0359 
0360 // ----------------------------- Busy Animation ----------------------------- //
0361 
0362 Spinner = fun() {
0363     // FIXME: try to use this=
0364     spinner = global.Spinner | [];
0365     spinner.count = 360;
0366     spinner.current_idx = 0;
0367     spinner.last_time = 0;
0368     spinner.steps = 10.0; // We render degrees in increments of 10 to save disk.
0369     spinner.duration = 1.5; // Seconds per rotation.
0370     for (i = 0; i <= spinner.count; ++i) {
0371         if (i % spinner.steps != 0) {
0372             continue;
0373         }
0374         spinner[i] = SpriteImage(assets.spinner_base + "/spinner" + i + ".png");
0375         center_offset = (logo.width / 2) - (spinner[i].width / 2);
0376         top_offset = logo.height + spinner[i].height;
0377         spinner[i].SetPosition(logo.GetX() + center_offset, logo.GetY() + top_offset, logo.GetZ());
0378         spinner[i].SetOpacity(0);
0379     }
0380     return spinner;
0381 } | [];
0382 
0383 Spinner.Animate = fun(time) {
0384     degrees = Math.Int(((2 * Math.Pi / duration) * time) * (180 / Math.Pi));
0385     new = degrees % count;
0386     old = current_idx;
0387     if (Math.Int(new) < Math.Int((old + steps) % count)) {
0388         // Every $steps degrees we can render a frame, all others we skip.
0389         return;
0390     }
0391     // We set a second new which is now a correct index bump by coercing it
0392     // into a multiple of 10.
0393     new = Math.Int(new / steps) * steps;
0394     // Debug("going from " + old + " to " + new);
0395     // dps = time - last_time;
0396     // DebugMedium("dps " + dps*35);
0397     // last_time = time;
0398     this[old].SetOpacity(0);
0399     this[new].SetOpacity(1);
0400     current_idx = new;
0401     return this;
0402 };
0403 
0404 Spinner.GetY = fun() {
0405     return this[0].GetY();
0406 };
0407 
0408 Spinner.GetHeight = fun() {
0409     return this[0].height;
0410 };
0411 
0412 global.spin = Spinner();
0413 
0414 // ---------------------------- State & Spacing ----------------------------- //
0415 
0416 message_notification[0].image = ImageToTintedText ("");
0417 message_notification[1].image = ImageToTintedText ("");
0418 fsck_notification.image = ImageToActionText ("");
0419 
0420 status = "normal";
0421 
0422 // use a fixed string with ascending and descending stems to calibrate the
0423 // bounding box for the first message, so the messages below don't move up
0424 // and down according to *their* height.
0425 first_line_height = ImageToTintedText ("AfpqtM").GetHeight();
0426 
0427 // if the user has a 640x480 or 800x600 display, we can't quite fit everything
0428 // (including passphrase prompts) with the target spacing, so scoot the text up
0429 // a bit if needed.
0430 top_of_the_text = TextYOffset();
0431 
0432 // ----------------------------- Boot Progress ------------------------------ //
0433 
0434 /**
0435  * Implement boot progress callback
0436  * \param time time elapsed since boot start (real, seconds)
0437  * \param progress boot progress in % (real 0.0 to 1.0)
0438  */
0439 fun boot_progress_cb(time, progress) {
0440     spin.Animate(time);
0441     logo.SetOpacity_(time * 2.0);
0442 }
0443 Plymouth.SetBootProgressFunction (boot_progress_cb);
0444 
0445 #-----------------------------------------Label utility functions---------------------
0446 
0447 # label should be either a string or NULL
0448 # Images for n lines will be created and returned as items of the
0449 # message_label array
0450 #
0451 fun get_message_label (label, is_fake, is_action_line) {
0452     # Debug("Get Label position");
0453 
0454     if (is_fake)
0455         # Create a fake label so as to get the y coordinate of
0456         # a standard-length label.
0457         local.message_image = ImageToTintedText ("This is a fake message");
0458     else
0459         local.message_image = (is_action_line) && ImageToActionText (label) || ImageToTintedText (label);
0460 
0461     local.message_label = [];
0462     message_label.width = message_image.GetWidth ();
0463     message_label.height = message_image.GetHeight ();
0464 
0465     # Center the line horizontally
0466     message_label.x = Window.GetX () + Window.GetWidth () / 2 - message_label.width / 2;
0467 
0468     message_label.y = top_of_the_text + first_line_height/2;
0469 
0470     # Put the 2nd line below the fsck line
0471     if (is_action_line) {
0472         local.fsck_label.y = message_label.y + (first_line_height + first_line_height / 2);
0473         message_label.y = local.fsck_label.y + (first_line_height * 2);
0474     }
0475 
0476     # Debug("action label x = " + message_label.x + " y = " + message_label.y );
0477 
0478 #    message_debug = "msg_x = " + message_label.x + " msg_y = " + message_label.y +
0479 #                    "msg_width = " + message_label.width + " msg_height = " +
0480 #                    message_label.height + " message = " + label;
0481 #    Debug(message_debug);
0482 
0483     return message_label;
0484 
0485 }
0486 
0487 # Create an fsck label and/or get its position
0488 fun get_fsck_label (label, is_fake) {
0489     # Debug("Get Label position");
0490     local.fsck_label = global.progress_label;
0491 
0492     if (is_fake)
0493         fsck_label.image = ImageToTintedText ("This is a fake message");
0494     else
0495         fsck_label.image = ImageToTintedText (label);
0496 
0497     fsck_label.width = fsck_label.image.GetWidth ();
0498     fsck_label.height = fsck_label.image.GetHeight ();
0499 
0500     # Centre the label horizontally
0501     fsck_label.x = Window.GetX () + Window.GetWidth () / 2 - fsck_label.width / 2;
0502 
0503     local.first_label = get_message_label (label, 1, 0);
0504 
0505     # Place the label below the 1st message line
0506     fsck_label.y = local.first_label.y + local.first_label.height + first_line_height / 2;
0507 
0508 #    message_debug = "msg_x = " + fsck_label.x + " msg_y = " + fsck_label.y +
0509 #                    "msg_width = " + fsck_label.width + " msg_height = " +
0510 #                    fsck_label.height + " message = " + label;
0511 #    Debug(message_debug);
0512 
0513     return fsck_label;
0514 }
0515 
0516 #-----------------------------------------Message stuff --------------------------------
0517 #
0518 
0519 # Set up a message label
0520 #
0521 # NOTE: this is called when doing something like 'plymouth message "hello world"'
0522 #
0523 fun setup_message (message_text, x, y, z, index) {
0524     # Debug("Message setup");
0525     global.message_notification[index].image =
0526         (index) && ImageToActionText (message_text) || ImageToTintedText (message_text);
0527 
0528     # Set up the text message, if any
0529     message_notification[index].x = x;
0530     message_notification[index].y = y;
0531     message_notification[index].z = z;
0532 
0533     message_notification[index].sprite = Sprite ();
0534     message_notification[index].sprite.SetImage (message_notification[index].image);
0535     message_notification[index].sprite.SetX (message_notification[index].x);
0536     message_notification[index].sprite.SetY (message_notification[index].y);
0537     message_notification[index].sprite.SetZ (message_notification[index].z);
0538 
0539 }
0540 
0541 fun show_message (index) {
0542     if (global.message_notification[index].sprite) global.message_notification[index].sprite.SetOpacity(1);
0543 }
0544 
0545 fun hide_message (index) {
0546     if (global.message_notification[index].sprite) global.message_notification[index].sprite.SetOpacity(0);
0547 }
0548 
0549 # the callback function is called when new message should be displayed.
0550 # First arg is message to display.
0551 fun message_callback (message)
0552 {
0553     // DebugMedium("Message callback " + message);
0554     is_fake = 0;
0555     if (!message || (message == "")) is_fake = 1;
0556 
0557     local.substring = "keys:";
0558 
0559     # Look for the "keys:" prefix
0560     local.keys = StringString(message, local.substring);
0561 
0562     local.is_action_line = (keys != NULL);
0563     #Debug("keys " + local.keys + " substring length = " + StringLength(local.substring);
0564 
0565     # Get the message without the "keys:" prefix
0566     if (keys != NULL)
0567         message = StringCopy (message, keys + StringLength(local.substring), NULL);
0568 
0569     // Get the message without the "fsckd-cancel-msg" prefix as we don't support i18n
0570     substring = "fsckd-cancel-msg:";
0571     keys = StringString(message, substring);
0572     if (keys != NULL)
0573         message = StringCopy(message, keys + StringLength(substring), NULL);
0574 
0575     local.label.is_fake = is_fake;
0576     label = get_message_label(message, is_fake, is_action_line);
0577     label.z = 10000;
0578 
0579     setup_message (message, label.x, label.y, label.z, is_action_line);
0580     if (prompt_active && local.is_action_line)
0581         hide_message (is_action_line);
0582     else
0583         show_message (is_action_line);
0584 }
0585 Plymouth.SetMessageFunction (message_callback);
0586 
0587 
0588 #-----------------------------------------Display Password stuff -----------------------
0589 #
0590 
0591 fun password_dialog_setup (message_label) {
0592     # Debug("Password dialog setup");
0593 
0594     local.bullet_image = WriteText("•", palette.text.contrast);
0595     local.entry = [];
0596     entry.image = Image (assets.text_input);
0597 
0598     # Hide the normal labels
0599     prompt_active = 1;
0600     if (message_notification[1].sprite) hide_message (1);
0601 
0602     # Set the prompt label
0603     label = get_message_label(message_label, 0, 1);
0604     label.z = 10000;
0605 
0606     setup_message (message_label, label.x, label.y, label.z, 2);
0607     show_message (2);
0608 
0609     # Set up the text entry which contains the bullets
0610     entry.sprite = Sprite ();
0611     entry.sprite.SetImage (entry.image);
0612 
0613     # Centre the box horizontally
0614     entry.x = Window.GetX () + Window.GetWidth () / 2 - entry.image.GetWidth () / 2;
0615 
0616     # Put the entry below the second label.
0617     entry.y = message_notification[2].y + label.height + entry.image.GetHeight() / 2;
0618 
0619     #Debug ("entry x = " + entry.x + ", y = " + entry.y);
0620     entry.z = 10000;
0621     entry.sprite.SetX (entry.x);
0622     entry.sprite.SetY (entry.y);
0623     entry.sprite.SetZ (entry.z);
0624 
0625     global.password_dialog = local;
0626 }
0627 
0628 fun password_dialog_opacity (opacity) {
0629     # Debug("Password dialog opacity");
0630     global.password_dialog.opacity = opacity;
0631     local = global.password_dialog;
0632 
0633     # You can make the box translucent with a float
0634     # entry.sprite.SetOpacity (0.3);
0635     entry.sprite.SetOpacity (opacity);
0636     label.sprite.SetOpacity (opacity);
0637 
0638     if (bullets) {
0639         for (index = 0; bullets[index]; index++) {
0640             bullets[index].sprite.SetOpacity (opacity);
0641         }
0642     }
0643 }
0644 
0645 
0646 # The callback function is called when the display should display a password dialog.
0647 # First arg is prompt string, the second is the number of bullets.
0648 fun display_password_callback (prompt, bullets) {
0649     global.status = "password";
0650     if (!global.password_dialog)
0651         password_dialog_setup(prompt);
0652     password_dialog_opacity (1);
0653     bullet_width = password_dialog.bullet_image.GetWidth();
0654     bullet_y = password_dialog.entry.y +
0655                password_dialog.entry.image.GetHeight () / 2 -
0656                password_dialog.bullet_image.GetHeight () / 2;
0657     margin = bullet_width;
0658     spaces = Math.Int((password_dialog.entry.image.GetWidth () - (margin * 2)) / bullet_width);
0659     bullets_area.width = (margin * 2) + (spaces * bullet_width);
0660     bullets_area.x = Window.GetX () + Window.GetWidth () / 2 - bullets_area.width / 2;
0661     if (bullets > spaces)
0662         bullets = spaces;
0663     for (index = 0; password_dialog.bullets[index] || index < bullets; index++){
0664         if (!password_dialog.bullets[index]) {
0665             password_dialog.bullets[index].sprite = Sprite();
0666             password_dialog.bullets[index].sprite.SetImage (password_dialog.bullet_image);
0667             password_dialog.bullets[index].x = bullets_area.x + margin + index * bullet_width;
0668             password_dialog.bullets[index].sprite.SetX (password_dialog.bullets[index].x);
0669             password_dialog.bullets[index].y = bullet_y;
0670             password_dialog.bullets[index].sprite.SetY (password_dialog.bullets[index].y);
0671             password_dialog.bullets[index].z = password_dialog.entry.z + 1;
0672             password_dialog.bullets[index].sprite.SetZ (password_dialog.bullets[index].z);
0673         }
0674 
0675         password_dialog.bullets[index].sprite.SetOpacity (0);
0676 
0677         if (index < bullets) {
0678             password_dialog.bullets[index].sprite.SetOpacity (1);
0679         }
0680     }
0681 }
0682 Plymouth.SetDisplayPasswordFunction(display_password_callback);
0683 
0684 #----------------------------------------- FSCK Counter --------------------------------
0685 
0686 # Initialise the counter
0687 fun init_fsck_count () {
0688     # The number of fsck checks in this cycle
0689     global.counter.total = 0;
0690     # The number of fsck checks already performed + the current one
0691     global.counter.current = 1;
0692     # The previous fsck
0693     global.counter.last = 0;
0694 }
0695 
0696 # Increase the total counter
0697 fun increase_fsck_count () {
0698     global.counter.total++;
0699 }
0700 
0701 fun increase_current_fsck_count () {
0702     global.counter.last = global.counter.current++;
0703 }
0704 
0705 # Clear the counter
0706 fun clear_fsck_count () {
0707     global.counter = NULL;
0708     init_fsck_count ();
0709 }
0710 
0711 // ----------------------------------------- Progress Label ------------------------------
0712 
0713 
0714 # Change the opacity level of a progress label
0715 #
0716 # opacity = 1 -> show
0717 # opacity = 0 -> hide
0718 # opacity = 0.3 (or any other float) -> translucent
0719 #
0720 fun set_progress_label_opacity (opacity) {
0721     # the label
0722     progress_label.sprite.SetOpacity (opacity);
0723 
0724     # Make the slot available again when hiding the bar
0725     # So that another bar can take its place
0726     if (opacity == 0) {
0727         progress_label.is_available = 1;
0728         progress_label.device = "";
0729     }
0730 }
0731 
0732 # Set up a new Progress Bar
0733 #
0734 # TODO: Make it possible to reuse (rather than recreate) a bar
0735 #       if .is_available = 1. Ideally this would just reset the
0736 #       label, the associated
0737 #       device and the image size of the sprite.
0738 
0739 fun init_progress_label (device, status_string) {
0740     # Make the slot unavailable
0741     global.progress_label.is_available = 0;
0742     progress_label.progress = 0;
0743     progress_label.device = device;
0744     progress_label.status_string = status_string;
0745 }
0746 
0747 # See if the progress label is keeping track of the fsck
0748 # of "device"
0749 #
0750 fun device_has_progress_label (device) {
0751     #DebugBottom ("label device = " + progress_label.device + " checking device " + device);
0752     return (progress_label.device == device);
0753 }
0754 
0755 # Update the Progress bar which corresponds to index
0756 #
0757 fun update_progress_label (progress) {
0758     # If progress is NULL then we just refresh the label.
0759     # This happens when only counter.total has changed.
0760     if (progress != NULL) {
0761         progress_label.progress = progress;
0762 
0763         #Debug("device " + progress_label.device + " progress " + progress);
0764 
0765         # If progress >= 100% hide the label and make it available again
0766         if (progress >= 100) {
0767             set_progress_label_opacity (0);
0768 
0769             # See if we any other fsck check is complete
0770             # and, if so, hide the progress bars and the labels
0771             on_fsck_completed ();
0772 
0773             return 0;
0774         }
0775     }
0776     # Update progress label here
0777     #
0778     # FIXME: the queue logic from this theme should really be moved into mountall
0779     # instead of using string replacement to deal with localised strings.
0780     label = StringReplace (progress_label.status_string[0], "%1$d", global.counter.current);
0781     label = StringReplace (label, "%2$d",  global.counter.total);
0782     label = StringReplace (label, "%3$d",  progress_label.progress);
0783     label = StringReplace (label, "%%",  "%");
0784 
0785     progress_label = get_fsck_label (label, 0);
0786     #progress_label.progress = progress;
0787 
0788     progress_label.sprite = Sprite (progress_label.image);
0789 
0790     # Set up the bar
0791     progress_label.sprite.SetPosition(progress_label.x, progress_label.y, 1);
0792 
0793     set_progress_label_opacity (1);
0794 
0795 }
0796 
0797 # Refresh the label so as to update counters
0798 fun refresh_progress_label () {
0799     update_progress_label (NULL);
0800 }
0801 
0802 #----------------------------------------- FSCK Queue ----------------------------------
0803 
0804 # Initialise the fsck queue
0805 fun init_queue () {
0806     global.fsck_queue[0].device;
0807     global.fsck_queue[0].progress;
0808     global.fsck_queue.counter = 0;
0809     global.fsck_queue.biggest_item = 0;
0810 }
0811 
0812 fun clear_queue () {
0813     global.fsck_queue = NULL;
0814     init_queue ();
0815 }
0816 
0817 # Return either the device index in the queue or -1
0818 fun queue_look_up_by_device (device) {
0819     for (i=0; i <= fsck_queue.biggest_item; i++) {
0820         if ((fsck_queue[i]) && (fsck_queue[i].device == device))
0821             return i;
0822     }
0823     return -1;
0824 }
0825 
0826 # Keep track of an fsck process in the queue
0827 fun add_fsck_to_queue (device, progress) {
0828     # Look for an empty slot in the queue
0829     for (i=0; global.fsck_queue[i].device; i++) {
0830         continue;
0831     }
0832     local.index = i;
0833 
0834     # Set device and progress
0835     global.fsck_queue[local.index].device = device;
0836     global.fsck_queue[local.index].progress = progress;
0837 
0838     # Increase the queue counter
0839     global.fsck_queue.counter++;
0840 
0841     # Update the max index of the array for iterations
0842     if (local.index > global.fsck_queue.biggest_item)
0843         global.fsck_queue.biggest_item = local.index;
0844 
0845     #DebugMedium ("Adding " + device + " at " + local.index);
0846 }
0847 
0848 fun is_queue_empty () {
0849     return (fsck_queue.counter == 0);
0850 }
0851 
0852 fun is_progress_label_available () {
0853     return (progress_label.is_available == 1);
0854 }
0855 
0856 
0857 # This should cover the case in which the fsck checks in
0858 # the queue are completed before the ones showed in the
0859 # progress label
0860 fun on_queued_fsck_completed () {
0861     if (!is_queue_empty ())
0862         return;
0863 
0864     # Hide the extra label, if any
0865     #if (progress_bar.extra_label.sprite)
0866     #    progress_bar.extra_label.sprite.SetOpacity(0);
0867 }
0868 
0869 fun remove_fsck_from_queue (index) {
0870     # Free memory which was previously allocated for
0871     # device and progress
0872     global.fsck_queue[index].device = NULL;
0873     global.fsck_queue[index].progress = NULL;
0874 
0875     # Decrease the queue counter
0876     global.fsck_queue.counter--;
0877 
0878     # See if there are other processes in the queue
0879     # if not, clear the extra_label
0880     on_queued_fsck_completed ();
0881 }
0882 
0883 fun on_fsck_completed () {
0884     # We have moved on to tracking the next fsck
0885     increase_current_fsck_count ();
0886 
0887     if (!is_progress_label_available ())
0888         return;
0889 
0890     if (!is_queue_empty ())
0891         return;
0892 
0893     # Hide the progress label
0894     if (progress_label.sprite)
0895         progress_label.sprite.SetOpacity (0);
0896 
0897     # Clear the queue
0898     clear_queue ();
0899 
0900     # Clear the fsck counter
0901     clear_fsck_count ();
0902 }
0903 
0904 # Update an fsck process that we keep track of in the queue
0905 fun update_progress_in_queue (index, device, progress) {
0906     # If the fsck is complete, remove it from the queue
0907     if (progress >= 100) {
0908         remove_fsck_from_queue (index);
0909         on_queued_fsck_completed ();
0910         return;
0911     }
0912 
0913     global.fsck_queue[index].device = device;
0914     global.fsck_queue[index].progress = progress;
0915 
0916 }
0917 
0918 # TODO: Move it to some function
0919 # Create an empty queue
0920 #init_queue ();
0921 
0922 
0923 #----------------------------------------- FSCK Functions ------------------------------
0924 
0925 
0926 # Either add a new bar for fsck checks or update an existing bar
0927 #
0928 # NOTE: no more than "progress_bar.max_number" bars are allowed
0929 #
0930 fun fsck_check (device, progress, status_string) {
0931 
0932     # The 1st time this will take place
0933     if (!global.progress_label) {
0934         # Increase the fsck counter
0935         increase_fsck_count ();
0936 
0937         # Set up a new label for the check
0938         init_progress_label (device, status_string);
0939         update_progress_label (progress);
0940 
0941         return;
0942     }
0943 
0944 
0945     if (device_has_progress_label (device)) {
0946         // Update the progress of the existing label
0947         update_progress_label (progress);
0948     }
0949     else {
0950         //  See if there's already a slot in the queue for the device
0951         local.queue_device_index = queue_look_up_by_device(device);
0952 
0953         // See if the progress_label is available
0954         if (progress_label.is_available) {
0955             # If the fsck check for the device was in the queue, then
0956             # remove it from the queue
0957             if (local.queue_device_index >= 0) {
0958                 remove_fsck_from_queue (index);
0959             }
0960             else {
0961                 # Increase the fsck counter
0962                 increase_fsck_count ();
0963             }
0964 
0965             // Set up a new label for the check
0966             init_progress_label (device, status_string);
0967             update_progress_label (progress);
0968 
0969         }
0970         # If the progress_label is not available
0971         else {
0972 
0973             # If the fsck check for the device is already in the queue
0974             # just update its progress in the queue
0975             if (local.queue_device_index >= 0) {
0976                 #DebugMedium("Updating queue at " + local.queue_device_index + " for device " + device);
0977                 update_progress_in_queue (local.queue_device_index, device, progress);
0978             }
0979             # Otherwise add the check to the queue
0980             else {
0981                 #DebugMedium("Adding device " + device + " to queue at " + local.queue_device_index);
0982                 add_fsck_to_queue (device, progress);
0983 
0984                 # Increase the fsck counter
0985                 increase_fsck_count ();
0986 
0987                 refresh_progress_label ();
0988             }
0989 
0990         }
0991     }
0992 }
0993 
0994 
0995 #-----------------------------------------Update Status stuff --------------------------
0996 #
0997 # The update_status_callback is what we can use to pass plymouth whatever we want so
0998 # as to make use of features which are available only in this program (as opposed to
0999 # being available for any theme for the script plugin).
1000 #
1001 # Example:
1002 #
1003 #   Thanks to the current implementation, some scripts can call "plymouth --update=fsck:sda1:40"
1004 #   and this program will know that 1) we're performing and fsck check, 2) we're checking sda1,
1005 #   3) the program should set the label progress to 40%
1006 #
1007 # Other features can be easily added by parsing the string that we pass plymouth with "--update"
1008 #
1009 fun update_status_callback (status) {
1010     // Debug(" STATUS:" + status);
1011     if (!status) return;
1012 
1013     string_it = 0;
1014     update_strings[string_it] = "";
1015 
1016     for (i=0; (String(status).CharAt(i) != ""); i++) {
1017         local.temp_char = String(status).CharAt(i);
1018         if (temp_char != ":")
1019             update_strings[string_it] += temp_char;
1020         else
1021             update_strings[++string_it] = "";
1022     }
1023 
1024     // Let's assume that we're dealing with these strings fsck:sda1:40
1025     if ((string_it >= 2) && (update_strings[0] == "fsck")) {
1026 
1027         device = update_strings[1];
1028         progress = update_strings[2];
1029         status_string[0] = update_strings[3]; # "Checking disk %1$d of %2$d (%3$d %% complete)"
1030         if (!status_string[0])
1031             status_string[0] = "Checking disk %1$d of %2$d (%3$d %% complete)";
1032 
1033         if ((device != "") && (progress != "")) {
1034             progress = StringToInteger (progress);
1035 
1036             # Make sure that the fsck_queue is initialised
1037             if (!global.fsck_queue)
1038                 init_queue ();
1039 
1040             # Make sure that the fsck counter is initialised
1041             if (!global.counter)
1042                 init_fsck_count ();
1043 
1044 #            if (!global.progress_bar.extra_label.sprite)
1045 #                create_extra_fsck_label ();
1046 
1047             # Keep track of the fsck check
1048             fsck_check (device, progress, status_string);
1049         }
1050 
1051     }
1052 
1053     # systemd-fsckd pass fsckd:<number_devices>:<progress>:<l10n_string>
1054     if (update_strings[0] == "fsckd") {
1055         number_devices = StringToInteger(update_strings[1]);
1056 
1057         if (number_devices > 0) {
1058             label = update_strings[3];
1059 
1060             progress_label = get_fsck_label (label, 0);
1061             progress_label.sprite = Sprite (progress_label.image);
1062             progress_label.sprite.SetPosition(progress_label.x, progress_label.y, 1);
1063             progress_label.sprite.SetOpacity (1);
1064         } else {
1065             if (progress_label.sprite)
1066                 progress_label.sprite.SetOpacity (0);
1067         }
1068     }
1069 
1070 }
1071 Plymouth.SetUpdateStatusFunction (update_status_callback);
1072 
1073 /**
1074  * Calling Plymouth.SetRefreshFunction with a function will set that function to be
1075  * called up to 50 times every second, e.g.
1076  *
1077  * NOTE: if a refresh function is not set, Plymouth doesn't seem to be able to update
1078  *      the screen correctly
1079  */
1080 fun refresh_callback() {
1081     // With some nvidia systems when using the script theme the initial
1082     // background drawing happens too soon, so we do an additional "fallback"
1083     // draw run when the first refresh callback arrives. This should make sure
1084     // that we always have a background drawn.
1085     if (!global.backgroundApplied) {
1086         global.backgroundApplied = true;
1087         Window.ApplyBackgroundColors();
1088     }
1089 }
1090 Plymouth.SetRefreshFunction(refresh_callback);
1091 
1092 /**
1093  * The callback function is called when the display should return to normal
1094  */
1095 fun display_normal_callback() {
1096     global.status = "normal";
1097     if (global.password_dialog) {
1098         password_dialog_opacity (0);
1099         global.password_dialog = NULL;
1100         if (message_notification[2].sprite) hide_message(2);
1101         prompt_active = 0;
1102     }
1103 
1104     if (message_notification[1].sprite)
1105         show_message (1);
1106 }
1107 Plymouth.SetDisplayNormalFunction (display_normal_callback);
1108 
1109 /**
1110  * Switch to final state.
1111  */
1112 fun quit_callback() {
1113   logo.SetOpacity_(0);
1114 }
1115 Plymouth.SetQuitFunction(quit_callback);
1116 
1117 // kate: space-indent on; indent-width 2; mixedindent off; indent-mode cstyle; hl JavaScript;