From b1aa85c1e828cf4350cdd153cb66a3bec835be01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A5=D0=B8=D1=80=D0=B5=D1=86=D0=BA=D0=B8=D0=B9=20=D0=9C?= =?UTF-8?q?=D0=B8=D1=85=D0=B0=D0=B8=D0=BB?= Date: Wed, 2 Dec 2020 18:05:05 +0300 Subject: [PATCH] =?UTF-8?q?sys-boot/plymouth-calculate-plugin:=20=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D1=81=D0=B8=D1=8F=200.9.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sys-boot/plymouth-calculate-plugin/Manifest | 1 + .../files/0.9.5-calculate.plymouth | 74 + .../files/0.9.5-plugin.c | 2223 +++++++++++++++++ .../plymouth-calculate-plugin-0.9.5.ebuild | 98 + 4 files changed, 2396 insertions(+) create mode 100644 sys-boot/plymouth-calculate-plugin/files/0.9.5-calculate.plymouth create mode 100644 sys-boot/plymouth-calculate-plugin/files/0.9.5-plugin.c create mode 100644 sys-boot/plymouth-calculate-plugin/plymouth-calculate-plugin-0.9.5.ebuild diff --git a/sys-boot/plymouth-calculate-plugin/Manifest b/sys-boot/plymouth-calculate-plugin/Manifest index 8c959d026..c41ebb209 100644 --- a/sys-boot/plymouth-calculate-plugin/Manifest +++ b/sys-boot/plymouth-calculate-plugin/Manifest @@ -1 +1,2 @@ DIST plymouth-0.9.4.tar.xz 1103496 BLAKE2B abad2cefb89e9d17b77113396d7203090544759b3dd7f73f67153d9f126db28e5366d36aa11e2335566bf4046bebd50d8d9dd47c75634446fb3a0314cd458925 SHA512 83eb2de7e6d0980e9f7fa4e0b0f20c46a8238051d84bc38dbbb5dfa438e41c1a39846dcd652374256d9f1fe79967b154a3576cd9c433ef816b6c962be2d31e93 +DIST plymouth-0.9.5.tar.xz 1186200 BLAKE2B 8fd073703773fcf7f3e26454c860f094fb4b3d712c56c9df6716ef11815a79f7d5b078ab6c6567b76421026d84a086f95865bf9f3e4ae6363cb1ffc2caa68537 SHA512 686220e8d5b1a8be298156786d979d0e3fb9e010b930d0e8082a2bb152cf07c1bf493d820c243838a1771ee859dc0b4723bd9f10ee434a94a096ce9236c36ce9 diff --git a/sys-boot/plymouth-calculate-plugin/files/0.9.5-calculate.plymouth b/sys-boot/plymouth-calculate-plugin/files/0.9.5-calculate.plymouth new file mode 100644 index 000000000..d23ed1b73 --- /dev/null +++ b/sys-boot/plymouth-calculate-plugin/files/0.9.5-calculate.plymouth @@ -0,0 +1,74 @@ +[Plymouth Theme] +Name=Calculate +Description=Calculate spinner theme +ModuleName=calculate + +[calculate] +Font=DroidSans 12 +TitleFont=DroidSans 30 +ImageDir=/usr/share/plymouth/themes/calculate +DialogHorizontalAlignment=.5 +DialogVerticalAlignment=.382 +TitleHorizontalAlignment=.5 +TitleVerticalAlignment=.5 +HorizontalAlignment=.5 +VerticalAlignment=.5 +WatermarkHorizontalAlignment=.5 +WatermarkVerticalAlignment=.5 +Transition=none +TransitionDuration=0.0 +BackgroundStartColor=0x000000 +BackgroundEndColor=0x000000 +ProgressBarBackgroundColor=0x606060 +ProgressBarForegroundColor=0xffffff +DialogClearsFirmwareBackground=true +MessageBelowAnimation=true + +[boot-up] +UseEndAnimation=false +UseFirmwareBackground=false +VerticalAlignmentType=below_watermark +VerticalAlignment=.0 +WatermarkImage=/usr/share/plymouth/themes/calculate/boot.png + +[shutdown] +UseEndAnimation=false +UseFirmwareBackground=false +Title=Shutdown... +TitleColor=0xaaaaaa +VerticalAlignment=.0 +VerticalAlignmentType=below_title +WatermarkImage=/usr/share/plymouth/themes/calculate/.shutdown + +[reboot] +UseEndAnimation=false +UseFirmwareBackground=false +Title=Reboot... +TitleColor=0xaaaaaa +VerticalAlignment=.0 +VerticalAlignmentType=below_title +WatermarkImage=/usr/share/plymouth/themes/calculate/.shutdown + +[updates] +UseFirmwareBackground=false +SuppressMessages=true +ProgressBarShowPercentComplete=true +UseProgressBar=true +Title=Installing Updates... +SubTitle=Do not turn off your computer + +[system-upgrade] +UseFirmwareBackground=false +SuppressMessages=true +ProgressBarShowPercentComplete=true +UseProgressBar=true +Title=Upgrading System... +SubTitle=Do not turn off your computer + +[firmware-upgrade] +UseFirmwareBackground=false +SuppressMessages=true +ProgressBarShowPercentComplete=true +UseProgressBar=true +Title=Upgrading Firmware... +SubTitle=Do not turn off your computer diff --git a/sys-boot/plymouth-calculate-plugin/files/0.9.5-plugin.c b/sys-boot/plymouth-calculate-plugin/files/0.9.5-plugin.c new file mode 100644 index 000000000..98d0c809a --- /dev/null +++ b/sys-boot/plymouth-calculate-plugin/files/0.9.5-plugin.c @@ -0,0 +1,2223 @@ +/* + * + * Copyright (C) 2009-2019 Red Hat, Inc. + * + * 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. + * + * Written by: William Jon McCann, Hans de Goede + * + */ +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ply-boot-splash-plugin.h" +#include "ply-buffer.h" +#include "ply-capslock-icon.h" +#include "ply-entry.h" +#include "ply-event-loop.h" +#include "ply-label.h" +#include "ply-list.h" +#include "ply-logger.h" +#include "ply-image.h" +#include "ply-key-file.h" +#include "ply-keymap-icon.h" +#include "ply-trigger.h" +#include "ply-pixel-buffer.h" +#include "ply-pixel-display.h" +#include "ply-utils.h" +#include "ply-i18n.h" + +#include "ply-animation.h" +#include "ply-progress-animation.h" +#include "ply-throbber.h" +#include "ply-progress-bar.h" + +#include + +#ifndef FRAMES_PER_SECOND +#define FRAMES_PER_SECOND 30 +#endif + +#ifndef SHOW_ANIMATION_FRACTION +#define SHOW_ANIMATION_FRACTION 0.9 +#endif + +#define PROGRESS_BAR_WIDTH 400 +#define PROGRESS_BAR_HEIGHT 5 + +#define BGRT_STATUS_ORIENTATION_OFFSET_0 (0 << 1) +#define BGRT_STATUS_ORIENTATION_OFFSET_90 (1 << 1) +#define BGRT_STATUS_ORIENTATION_OFFSET_180 (2 << 1) +#define BGRT_STATUS_ORIENTATION_OFFSET_270 (3 << 1) +#define BGRT_STATUS_ORIENTATION_OFFSET_MASK (3 << 1) + +typedef enum +{ + PLY_BOOT_SPLASH_DISPLAY_NORMAL, + PLY_BOOT_SPLASH_DISPLAY_QUESTION_ENTRY, + PLY_BOOT_SPLASH_DISPLAY_PASSWORD_ENTRY +} ply_boot_splash_display_type_t; + +typedef enum +{ + PROGRESS_FUNCTION_TYPE_WWOODS, + PROGRESS_FUNCTION_TYPE_LINEAR, +} progress_function_t; + +typedef struct +{ + ply_boot_splash_plugin_t *plugin; + ply_pixel_display_t *display; + ply_entry_t *entry; + ply_keymap_icon_t *keymap_icon; + ply_capslock_icon_t *capslock_icon; + ply_animation_t *end_animation; + ply_progress_animation_t *progress_animation; + ply_progress_bar_t *progress_bar; + ply_throbber_t *throbber; + ply_label_t *label; + ply_label_t *message_label; + ply_label_t *title_label; + ply_label_t *subtitle_label; + ply_rectangle_t box_area, lock_area, watermark_area, title_area, dialog_area; + ply_trigger_t *end_trigger; + ply_pixel_buffer_t *background_buffer; + ply_image_t *watermark_image; + int animation_bottom; +} view_t; + +typedef struct +{ + bool suppress_messages; + bool progress_bar_show_percent_complete; + bool use_progress_bar; + bool use_animation; + bool use_end_animation; + bool use_firmware_background; + char *title; + char *subtitle; + uint32_t background_start_color; + uint32_t background_end_color; + uint32_t title_color; + char *watermark_imagename; + double animation_horizontal_alignment; + double animation_vertical_alignment; + char *animation_vertical_alignment_type; +} mode_settings_t; + +struct _ply_boot_splash_plugin +{ + ply_event_loop_t *loop; + ply_boot_splash_mode_t mode; + mode_settings_t mode_settings[PLY_BOOT_SPLASH_MODE_COUNT]; + char *font; + ply_image_t *lock_image; + ply_image_t *box_image; + ply_image_t *corner_image; + ply_image_t *header_image; + ply_image_t *background_tile_image; + ply_image_t *background_bgrt_image; + ply_list_t *views; + + ply_boot_splash_display_type_t state; + + double dialog_horizontal_alignment; + double dialog_vertical_alignment; + + double title_horizontal_alignment; + double title_vertical_alignment; + char *title_font; + + double watermark_horizontal_alignment; + double watermark_vertical_alignment; + char *watermark_imagename; + + double animation_horizontal_alignment; + double animation_vertical_alignment; + char *animation_vertical_alignment_type; + char *animation_dir; + + ply_progress_animation_transition_t transition; + double transition_duration; + + uint32_t background_start_color; + uint32_t background_end_color; + uint32_t title_color; + int background_bgrt_raw_width; + int background_bgrt_raw_height; + + double progress_bar_horizontal_alignment; + double progress_bar_vertical_alignment; + long progress_bar_width; + long progress_bar_height; + uint32_t progress_bar_bg_color; + uint32_t progress_bar_fg_color; + + progress_function_t progress_function; + + ply_trigger_t *idle_trigger; + ply_trigger_t *stop_trigger; + + uint32_t root_is_mounted : 1; + uint32_t is_visible : 1; + uint32_t is_animating : 1; + uint32_t is_idle : 1; + uint32_t use_firmware_background : 1; + uint32_t dialog_clears_firmware_background : 1; + uint32_t message_below_animation : 1; +}; + +ply_boot_splash_plugin_interface_t *ply_boot_splash_plugin_get_interface (void); + +static void stop_animation (ply_boot_splash_plugin_t *plugin); +static void detach_from_event_loop (ply_boot_splash_plugin_t *plugin); +static void display_message (ply_boot_splash_plugin_t *plugin, + const char *message); +static void become_idle (ply_boot_splash_plugin_t *plugin, + ply_trigger_t *idle_trigger); +static void view_show_message (view_t *view, const char *message); + +bool is_dir(const char* path) { + struct stat path_stat; + stat(path, &path_stat); + return S_ISDIR(path_stat.st_mode); +} + +void image_get_res(const char *basedir, int *xres, int *yres) +{ + FILE *fp; + char buf[512]; + int oxres, oyres; + + oxres = *xres; + oyres = *yres; + snprintf(buf, 512, "%s/%dx%d.png", basedir, oxres, oyres); + + fp = fopen(buf, "r"); + if (!fp) { + *xres = 1024; + *yres = 768; + unsigned int t, tx, ty, mdist = 0xffffffff; + struct dirent *dent; + DIR *tdir; + + snprintf(buf, 512, "%s", basedir); + tdir = opendir(buf); + if (!tdir) { + *xres = 0; + *yres = 0; + return; + } + while ((dent = readdir(tdir))) { + if (sscanf(dent->d_name, "%dx%d.png", &tx, &ty) != 2) + continue; + + /* We only want configs for resolutions smaller than the current one, + * so that we can actually fit the image on the screen. */ + if (tx >= oxres || ty >= oyres) + continue; + + t = (tx - oxres) * (tx - oxres) + (ty - oyres) * (ty - oyres); + + /* Penalize configs for resolutions with different aspect ratios. */ + if (oxres / oyres != tx / ty) + t *= 10; + + if (t < mdist) { + *xres = tx; + *yres = ty; + mdist = t; + } + } + closedir(tdir); + } else { + fclose(fp); + } +} + +char *detect_image(const char *logo_image, int xres, int yres) { + char *buf; + if(logo_image != NULL) { + if(is_dir(logo_image)) { + image_get_res(logo_image, &xres, &yres); + if(asprintf(&buf, "%s/%dx%d.png", logo_image, xres, yres) != -1) + return buf; + } else { + return strdup (logo_image); + } + } + return NULL; +} + +long +calculate_animation_y(view_t *view, double animation_vertical_alignment, + char *animation_vertical_alignment_type, unsigned long screen_height, long height) +{ + ply_boot_splash_plugin_t *plugin; + plugin = view->plugin; + long y; + if (animation_vertical_alignment_type == NULL) { + y = animation_vertical_alignment * screen_height - height / 2.0; + } else if (strcmp(animation_vertical_alignment_type, + "below_title") == 0) { + y = view->title_area.y + view->title_area.height; + if( y + height >= screen_height) { + y = animation_vertical_alignment * screen_height - height / 2.0; + } else { + y += animation_vertical_alignment * (screen_height - y); + if( animation_vertical_alignment != 0.0) { + y -= height / 2; + } + } + } else if (strcmp(animation_vertical_alignment_type, + "below_watermark") == 0 && view->watermark_image != NULL) { + y = view->watermark_area.y + view->watermark_area.height; + if( y + height >= screen_height) { + y = animation_vertical_alignment * screen_height - height / 2.0; + } else { + y += animation_vertical_alignment * (screen_height - y); + if( animation_vertical_alignment != 0.0) { + y -= height / 2; + } + } + } else { + y = animation_vertical_alignment * screen_height - height / 2.0; + } + return y; +} + +static view_t * +view_new (ply_boot_splash_plugin_t *plugin, + ply_pixel_display_t *display) +{ + view_t *view; + + view = calloc (1, sizeof(view_t)); + view->plugin = plugin; + view->display = display; + view->watermark_image = NULL; + + view->entry = ply_entry_new (plugin->animation_dir); + view->keymap_icon = ply_keymap_icon_new (display, plugin->animation_dir); + view->capslock_icon = ply_capslock_icon_new (plugin->animation_dir); + view->progress_animation = ply_progress_animation_new (plugin->animation_dir, + "progress-"); + ply_progress_animation_set_transition (view->progress_animation, + plugin->transition, + plugin->transition_duration); + + view->progress_bar = ply_progress_bar_new (); + ply_progress_bar_set_colors (view->progress_bar, + plugin->progress_bar_fg_color, + plugin->progress_bar_bg_color); + + view->throbber = ply_throbber_new (plugin->animation_dir, + "throbber-"); + + view->label = ply_label_new (); + ply_label_set_font (view->label, plugin->font); + + view->message_label = ply_label_new (); + ply_label_set_font (view->message_label, plugin->font); + + view->title_label = ply_label_new (); + ply_label_set_font (view->title_label, plugin->title_font); + + view->subtitle_label = ply_label_new (); + ply_label_set_font (view->subtitle_label, plugin->font); + + return view; +} + +static void +view_free (view_t *view) +{ + ply_entry_free (view->entry); + ply_keymap_icon_free (view->keymap_icon); + ply_capslock_icon_free (view->capslock_icon); + ply_animation_free (view->end_animation); + ply_progress_animation_free (view->progress_animation); + ply_progress_bar_free (view->progress_bar); + ply_throbber_free (view->throbber); + ply_label_free (view->label); + ply_label_free (view->message_label); + ply_label_free (view->title_label); + ply_label_free (view->subtitle_label); + + if (view->watermark_image != NULL) + ply_image_free (view->watermark_image); + + if (view->background_buffer != NULL) + ply_pixel_buffer_free (view->background_buffer); + + free (view); +} + +static void +view_load_end_animation (view_t *view) +{ + ply_boot_splash_plugin_t *plugin = view->plugin; + const char *animation_prefix; + + if (!plugin->mode_settings[plugin->mode].use_end_animation) + return; + + ply_trace ("loading animation"); + + switch (plugin->mode) { + case PLY_BOOT_SPLASH_MODE_BOOT_UP: + case PLY_BOOT_SPLASH_MODE_UPDATES: + case PLY_BOOT_SPLASH_MODE_SYSTEM_UPGRADE: + case PLY_BOOT_SPLASH_MODE_FIRMWARE_UPGRADE: + animation_prefix = "startup-animation-"; + break; + case PLY_BOOT_SPLASH_MODE_SHUTDOWN: + case PLY_BOOT_SPLASH_MODE_REBOOT: + animation_prefix = "shutdown-animation-"; + break; + case PLY_BOOT_SPLASH_MODE_INVALID: + default: + ply_trace ("unexpected splash mode 0x%x\n", plugin->mode); + return; + } + + ply_trace ("trying prefix: %s", animation_prefix); + view->end_animation = ply_animation_new (plugin->animation_dir, + animation_prefix); + + if (ply_animation_load (view->end_animation)) + return; + ply_animation_free (view->end_animation); + + ply_trace ("now trying more general prefix: animation-"); + view->end_animation = ply_animation_new (plugin->animation_dir, + "animation-"); + if (ply_animation_load (view->end_animation)) + return; + ply_animation_free (view->end_animation); + + ply_trace ("now trying old compat prefix: throbber-"); + view->end_animation = ply_animation_new (plugin->animation_dir, + "throbber-"); + if (ply_animation_load (view->end_animation)) { + /* files named throbber- are for end animation, so + * there's no throbber */ + ply_throbber_free (view->throbber); + view->throbber = NULL; + return; + } + + ply_trace ("optional animation didn't load"); + ply_animation_free (view->end_animation); + view->end_animation = NULL; + plugin->mode_settings[plugin->mode].use_end_animation = false; +} + +static bool +get_bgrt_sysfs_info(int *x_offset, int *y_offset, + ply_pixel_buffer_rotation_t *rotation) +{ + bool ret = false; + char buf[64]; + int status; + FILE *f; + + f = fopen("/sys/firmware/acpi/bgrt/status", "r"); + if (!f) + return false; + + if (!fgets(buf, sizeof(buf), f)) + goto out; + + if (sscanf(buf, "%d", &status) != 1) + goto out; + + fclose(f); + + switch (status & BGRT_STATUS_ORIENTATION_OFFSET_MASK) { + case BGRT_STATUS_ORIENTATION_OFFSET_0: + *rotation = PLY_PIXEL_BUFFER_ROTATE_UPRIGHT; + break; + case BGRT_STATUS_ORIENTATION_OFFSET_90: + *rotation = PLY_PIXEL_BUFFER_ROTATE_COUNTER_CLOCKWISE; + break; + case BGRT_STATUS_ORIENTATION_OFFSET_180: + *rotation = PLY_PIXEL_BUFFER_ROTATE_UPSIDE_DOWN; + break; + case BGRT_STATUS_ORIENTATION_OFFSET_270: + *rotation = PLY_PIXEL_BUFFER_ROTATE_CLOCKWISE; + break; + } + + f = fopen("/sys/firmware/acpi/bgrt/xoffset", "r"); + if (!f) + return false; + + if (!fgets(buf, sizeof(buf), f)) + goto out; + + if (sscanf(buf, "%d", x_offset) != 1) + goto out; + + fclose(f); + + f = fopen("/sys/firmware/acpi/bgrt/yoffset", "r"); + if (!f) + return false; + + if (!fgets(buf, sizeof(buf), f)) + goto out; + + if (sscanf(buf, "%d", y_offset) != 1) + goto out; + + ret = true; +out: + fclose(f); + return ret; +} + +/* The Microsoft boot logo spec says that the logo must use a black background + * and have its center at 38.2% from the screen's top (golden ratio). + * We reproduce this exactly here so that we get a background which is an exact + * match of the firmware's boot splash. + * At the time of writing this comment this is documented in a document called + * "Boot screen components" which is available here: + * https://docs.microsoft.com/en-us/windows-hardware/drivers/bringup/boot-screen-components + * Note that we normally do not use the firmware reported x and y-offset as + * that is based on the EFI fb resolution which may not be the native + * resolution of the screen (esp. when using multiple heads). + */ +static void +view_set_bgrt_background (view_t *view) +{ + ply_pixel_buffer_rotation_t panel_rotation = PLY_PIXEL_BUFFER_ROTATE_UPRIGHT; + ply_pixel_buffer_rotation_t bgrt_rotation = PLY_PIXEL_BUFFER_ROTATE_UPRIGHT; + int x_offset, y_offset, sysfs_x_offset, sysfs_y_offset, width, height; + int panel_width = 0, panel_height = 0, panel_scale = 1; + int screen_width, screen_height, screen_scale; + ply_pixel_buffer_t *bgrt_buffer; + bool have_panel_props; + + if (!view->plugin->background_bgrt_image) + return; + + if (!get_bgrt_sysfs_info(&sysfs_x_offset, &sysfs_y_offset, + &bgrt_rotation)) { + ply_trace ("get bgrt sysfs info failed"); + return; + } + + screen_width = ply_pixel_display_get_width (view->display); + screen_height = ply_pixel_display_get_height (view->display); + screen_scale = ply_pixel_display_get_device_scale (view->display); + + bgrt_buffer = ply_image_get_buffer (view->plugin->background_bgrt_image); + + have_panel_props = ply_renderer_get_panel_properties (ply_pixel_display_get_renderer (view->display), + &panel_width, &panel_height, + &panel_rotation, &panel_scale); + + /* + * Some buggy Lenovo 2-in-1s with a 90 degree rotated panel, behave as + * if the panel is mounted up-right / not rotated at all. These devices + * have a buggy efifb size (landscape resolution instead of the actual + * portrait resolution of the panel), this gets fixed-up by the kernel. + * These buggy devices also do not pre-rotate the bgrt_image nor do + * they set the ACPI-6.2 rotation status-bits. We can detect this by + * checking that the bgrt_image is perfectly centered horizontally + * when we use the panel's height as the width. + */ + if (have_panel_props && + (panel_rotation == PLY_PIXEL_BUFFER_ROTATE_CLOCKWISE || + panel_rotation == PLY_PIXEL_BUFFER_ROTATE_COUNTER_CLOCKWISE) && + (panel_width - view->plugin->background_bgrt_raw_width) / 2 != sysfs_x_offset && + (panel_height - view->plugin->background_bgrt_raw_width) / 2 == sysfs_x_offset) + bgrt_rotation = panel_rotation; + + /* + * Before the ACPI 6.2 specification, the BGRT table did not contain + * any rotation information, so to make sure that the firmware-splash + * showed the right way up the firmware would contain a pre-rotated + * image. Starting with ACPI 6.2 the bgrt status fields has 2 bits + * to tell the firmware the image needs to be rotated before being + * displayed. + * If these bits are set then the firmwares-splash is not pre-rotated, + * in this case we must not rotate it when rendering and when doing + * comparisons with the panel-size we must use the post rotation + * panel-size. + */ + if (bgrt_rotation != PLY_PIXEL_BUFFER_ROTATE_UPRIGHT) { + if (bgrt_rotation != panel_rotation) { + ply_trace ("bgrt orientation mismatch, bgrt_rot %d panel_rot %d", (int)bgrt_rotation, (int)panel_rotation); + return; + } + + /* Set panel properties to their post-rotations values */ + if (panel_rotation == PLY_PIXEL_BUFFER_ROTATE_CLOCKWISE || + panel_rotation == PLY_PIXEL_BUFFER_ROTATE_COUNTER_CLOCKWISE) { + int temp = panel_width; + panel_width = panel_height; + panel_height = temp; + } + panel_rotation = PLY_PIXEL_BUFFER_ROTATE_UPRIGHT; + } + + if (have_panel_props) { + ply_pixel_buffer_set_device_rotation (bgrt_buffer, panel_rotation); + ply_pixel_buffer_set_device_scale (bgrt_buffer, panel_scale); + } + + width = ply_pixel_buffer_get_width (bgrt_buffer); + height = ply_pixel_buffer_get_height (bgrt_buffer); + + x_offset = (screen_width - width) / 2; + y_offset = screen_height * 382 / 1000 - height / 2; + + /* + * On laptops / tablets the LCD panel is typically brought up in + * its native resolution, so we can trust the x- and y-offset values + * provided by the firmware to be correct for a screen with the panels + * resolution. + * + * Moreover some laptop / tablet firmwares to do all kind of hacks wrt + * the y offset. This happens especially on devices where the panel is + * mounted 90 degrees rotated, but also on other devices. + * + * So on devices with an internal LCD panel, we prefer to use the + * firmware provided offsets, to make sure we match its quirky behavior. + * + * We check that the x-offset matches what we expect for the panel's + * native resolution to make sure that the values are indeed for the + * panel's native resolution and then we correct for any difference + * between the (external) screen's and the panel's resolution. + */ + if (have_panel_props && + (panel_width - view->plugin->background_bgrt_raw_width) / 2 == sysfs_x_offset) { + if (panel_rotation == PLY_PIXEL_BUFFER_ROTATE_CLOCKWISE || + panel_rotation == PLY_PIXEL_BUFFER_ROTATE_COUNTER_CLOCKWISE) { + /* + * For left side up panels the y_offset is from the + * right side of the image once rotated upright (the + * top of the physicial LCD panel is on the right side). + * Our coordinates have the left side as 0, so we need + * to "flip" the y_offset in this case. + */ + if (panel_rotation == PLY_PIXEL_BUFFER_ROTATE_COUNTER_CLOCKWISE) + sysfs_y_offset = panel_height - view->plugin->background_bgrt_raw_height - sysfs_y_offset; + + /* 90 degrees rotated, swap x and y */ + x_offset = sysfs_y_offset / panel_scale; + y_offset = sysfs_x_offset / panel_scale; + + x_offset += (screen_width - panel_height / panel_scale) / 2; + y_offset += (screen_height - panel_width / panel_scale) * 382 / 1000; + } else { + /* Normal orientation */ + x_offset = sysfs_x_offset / panel_scale; + y_offset = sysfs_y_offset / panel_scale; + + x_offset += (screen_width - panel_width / panel_scale) / 2; + y_offset += (screen_height - panel_height / panel_scale) * 382 / 1000; + } + } + + /* + * On desktops (no panel) we normally do not use the BGRT provided + * xoffset and yoffset because the resolution they are intended for + * may be differtent then the resolution of the current display. + * + * On some desktops (no panel) the image gets centered not only + * horizontally, but also vertically. In this case our default of using + * the golden ratio for the vertical position causes the BGRT image + * to jump. To avoid this we check here if the provided xoffset and + * yoffset perfectly center the image and in that case we use them. + */ + if (!have_panel_props && screen_scale == 1 && + (screen_width - width ) / 2 == sysfs_x_offset && + (screen_height - height) / 2 == sysfs_y_offset) { + x_offset = sysfs_x_offset; + y_offset = sysfs_y_offset; + } + + ply_trace ("using %dx%d bgrt image centered at %dx%d for %dx%d screen", + width, height, x_offset, y_offset, screen_width, screen_height); + + view->background_buffer = ply_pixel_buffer_new (screen_width * screen_scale, screen_height * screen_scale); + ply_pixel_buffer_set_device_scale (view->background_buffer, screen_scale); + ply_pixel_buffer_fill_with_hex_color (view->background_buffer, NULL, 0x000000); + if (x_offset >= 0 && y_offset >= 0) { + bgrt_buffer = ply_pixel_buffer_rotate_upright (bgrt_buffer); + ply_pixel_buffer_fill_with_buffer (view->background_buffer, bgrt_buffer, x_offset, y_offset); + ply_pixel_buffer_free (bgrt_buffer); + } +} + +static bool +view_load (view_t *view) +{ + unsigned long x, y, width, title_height = 0, subtitle_height = 0; + unsigned long screen_width, screen_height, screen_scale; + char *image_dir, *image_path; + ply_boot_splash_plugin_t *plugin; + ply_pixel_buffer_t *buffer; + + plugin = view->plugin; + + screen_width = ply_pixel_display_get_width (view->display); + screen_height = ply_pixel_display_get_height (view->display); + + buffer = ply_renderer_get_buffer_for_head( + ply_pixel_display_get_renderer (view->display), + ply_pixel_display_get_renderer_head (view->display)); + screen_scale = ply_pixel_buffer_get_device_scale (buffer); + + view_set_bgrt_background (view); + + if (!view->background_buffer && plugin->background_tile_image != NULL) { + ply_trace ("tiling background to %lux%lu", screen_width, screen_height); + + /* Create a buffer at screen scale so that we only do the slow interpolating scale once */ + view->background_buffer = ply_pixel_buffer_new (screen_width * screen_scale, screen_height * screen_scale); + ply_pixel_buffer_set_device_scale (view->background_buffer, screen_scale); + + if (plugin->background_start_color != plugin->background_end_color) + ply_pixel_buffer_fill_with_gradient (view->background_buffer, NULL, + plugin->background_start_color, + plugin->background_end_color); + else + ply_pixel_buffer_fill_with_hex_color (view->background_buffer, NULL, + plugin->background_start_color); + + buffer = ply_pixel_buffer_tile (ply_image_get_buffer (plugin->background_tile_image), screen_width, screen_height); + ply_pixel_buffer_fill_with_buffer (view->background_buffer, buffer, 0, 0); + ply_pixel_buffer_free (buffer); + } + + if (view->watermark_image == NULL && plugin->mode_settings[plugin->mode].watermark_imagename) { + image_path = detect_image(plugin->mode_settings[plugin->mode].watermark_imagename, + screen_width, screen_height); + ply_trace ("assing watermark %s", image_path); + view->watermark_image = ply_image_new (image_path); + free (image_path); + } + if (view->watermark_image != NULL) { + ply_trace ("loading watermark image"); + if (!ply_image_load (view->watermark_image)) { + ply_image_free (view->watermark_image); + view->watermark_image = NULL; + } + } + + if (view->watermark_image != NULL) { + view->watermark_area.width = ply_image_get_width (view->watermark_image); + view->watermark_area.height = ply_image_get_height (view->watermark_image); + view->watermark_area.x = screen_width * plugin->watermark_horizontal_alignment - ply_image_get_width (view->watermark_image) * plugin->watermark_horizontal_alignment; + view->watermark_area.y = screen_height * plugin->watermark_vertical_alignment - ply_image_get_height (view->watermark_image) * plugin->watermark_vertical_alignment; + ply_trace ("using %ldx%ld watermark centered at %ldx%ld for %ldx%ld screen", + view->watermark_area.width, view->watermark_area.height, + view->watermark_area.x, view->watermark_area.y, + screen_width, screen_height); + } + + ply_trace ("loading entry"); + if (!ply_entry_load (view->entry)) + return false; + + ply_keymap_icon_load (view->keymap_icon); + ply_capslock_icon_load (view->capslock_icon); + + view_load_end_animation (view); + + if (view->progress_animation != NULL) { + ply_trace ("loading progress animation"); + if (!ply_progress_animation_load (view->progress_animation)) { + ply_trace ("optional progress animation wouldn't load"); + ply_progress_animation_free (view->progress_animation); + view->progress_animation = NULL; + } + } else { + ply_trace ("this theme has no progress animation"); + } + + if (view->throbber != NULL) { + ply_trace ("loading throbber"); + if (!ply_throbber_load (view->throbber)) { + ply_trace ("optional throbber was not loaded"); + ply_throbber_free (view->throbber); + view->throbber = NULL; + } + } else { + ply_trace ("this theme has no throbber\n"); + } + + if (plugin->mode_settings[plugin->mode].title) { + ply_label_set_text (view->title_label, + _(plugin->mode_settings[plugin->mode].title)); + title_height = ply_label_get_height (view->title_label); + } else { + ply_label_hide (view->title_label); + } + + if (plugin->mode_settings[plugin->mode].subtitle) { + ply_label_set_text (view->subtitle_label, + _(plugin->mode_settings[plugin->mode].subtitle)); + subtitle_height = ply_label_get_height (view->subtitle_label); + } else { + ply_label_hide (view->subtitle_label); + } + + y = (screen_height - title_height - 2 * subtitle_height) * plugin->title_vertical_alignment; + + view->title_area.width = 0; + view->title_area.height = 0; + view->title_area.x = -1; + view->title_area.y = 0; + if (plugin->mode_settings[plugin->mode].title) { + width = ply_label_get_width (view->title_label); + x = (screen_width - width) * plugin->title_horizontal_alignment; + ply_trace ("using %ldx%ld title centered at %ldx%ld for %ldx%ld screen", + width, title_height, x, y, screen_width, screen_height); + ply_label_show (view->title_label, view->display, x, y); + view->title_area.width = width; + view->title_area.height = 2 * title_height; + view->title_area.x = x; + view->title_area.y = y; + + /* Use subtitle_height pixels seperation between title and subtitle */ + y += title_height + subtitle_height; + } + + if (plugin->mode_settings[plugin->mode].subtitle) { + width = ply_label_get_width (view->subtitle_label); + x = (screen_width - width) * plugin->title_horizontal_alignment; + ply_trace ("using %ldx%ld subtitle centered at %ldx%ld for %ldx%ld screen", + width, subtitle_height, x, y, screen_width, screen_height); + ply_label_show (view->subtitle_label, view->display, x, y); + if( x == -1 || x < view->title_area.x ) { + view->title_area.x = x; + } + if( width > view->title_area.width ) { + view->title_area.width = width; + } + if (view->title_area.height != 0) { + view->title_area.height = title_height + subtitle_height + subtitle_height * 2; + } else { + view->title_area.height = subtitle_height * 2; + } + } + uint32_t title_color = 0xff00ff; + title_color = plugin->mode_settings[plugin->mode].title_color; + + if(view->title_label) { + ply_label_set_color(view->title_label, + ((title_color >> 16) & 0xff) / 255.0f, + ((title_color >> 8) & 0xff) / 255.0f, + (title_color & 0xff) / 255.0f, + 1.0f); + ply_label_set_color(view->subtitle_label, + ((title_color >> 16) & 0xff) / 255.0f, + ((title_color >> 8) & 0xff) / 255.0f, + (title_color & 0xff) / 255.0f, + 1.0f); + } + + return true; +} + +static bool +load_views (ply_boot_splash_plugin_t *plugin) +{ + ply_list_node_t *node; + bool view_loaded; + view_t *view; + + view_loaded = false; + node = ply_list_get_first_node (plugin->views); + + while (node != NULL) { + view = ply_list_node_get_data (node); + + if (view_load (view)) + view_loaded = true; + + node = ply_list_get_next_node (plugin->views, node); + } + + return view_loaded; +} + +static void +view_redraw (view_t *view) +{ + unsigned long screen_width, screen_height; + + screen_width = ply_pixel_display_get_width (view->display); + screen_height = ply_pixel_display_get_height (view->display); + + ply_pixel_display_draw_area (view->display, 0, 0, + screen_width, screen_height); +} + +static void +redraw_views (ply_boot_splash_plugin_t *plugin) +{ + ply_list_node_t *node; + view_t *view; + + node = ply_list_get_first_node (plugin->views); + while (node != NULL) { + view = ply_list_node_get_data (node); + view_redraw (view); + node = ply_list_get_next_node (plugin->views, node); + } +} + +static void +pause_views (ply_boot_splash_plugin_t *plugin) +{ + ply_list_node_t *node; + view_t *view; + + ply_trace ("pausing views"); + + node = ply_list_get_first_node (plugin->views); + while (node != NULL) { + view = ply_list_node_get_data (node); + ply_pixel_display_pause_updates (view->display); + node = ply_list_get_next_node (plugin->views, node); + } +} + +static void +unpause_views (ply_boot_splash_plugin_t *plugin) +{ + ply_list_node_t *node; + view_t *view; + + ply_trace ("unpausing views"); + + node = ply_list_get_first_node (plugin->views); + while (node != NULL) { + view = ply_list_node_get_data (node); + ply_pixel_display_unpause_updates (view->display); + node = ply_list_get_next_node (plugin->views, node); + } +} + +static void +view_start_end_animation (view_t *view, + ply_trigger_t *trigger) +{ + ply_boot_splash_plugin_t *plugin = view->plugin; + unsigned long screen_width, screen_height; + long x, y, width, height; + + ply_progress_bar_hide (view->progress_bar); + if (view->progress_animation != NULL) + ply_progress_animation_hide (view->progress_animation); + + screen_width = ply_pixel_display_get_width (view->display); + screen_height = ply_pixel_display_get_height (view->display); + width = ply_animation_get_width (view->end_animation); + height = ply_animation_get_height (view->end_animation); + x = plugin->mode_settings[plugin->mode].animation_horizontal_alignment * screen_width - width / 2.0; + y = calculate_animation_y(view, + plugin->mode_settings[plugin->mode].animation_vertical_alignment, + plugin->mode_settings[plugin->mode].animation_vertical_alignment_type, + screen_height, height); + + ply_trace ("starting end sequence animation for %ldx%ld view", width, height); + ply_animation_start (view->end_animation, + view->display, + trigger, x, y); + view->animation_bottom = y + height; +} + +static void +on_view_throbber_stopped (view_t *view) +{ + view_start_end_animation (view, view->end_trigger); + view->end_trigger = NULL; +} + +static void +view_start_progress_animation (view_t *view) +{ + ply_boot_splash_plugin_t *plugin; + + long x, y; + long width, height; + unsigned long screen_width, screen_height; + + assert (view != NULL); + + plugin = view->plugin; + + plugin->is_idle = false; + + screen_width = ply_pixel_display_get_width (view->display); + screen_height = ply_pixel_display_get_height (view->display); + + ply_pixel_display_draw_area (view->display, 0, 0, + screen_width, screen_height); + + if (plugin->mode_settings[plugin->mode].use_progress_bar) { + if (plugin->progress_bar_width != -1) + width = plugin->progress_bar_width; + else + width = screen_width; + height = plugin->progress_bar_height; + x = plugin->progress_bar_horizontal_alignment * (screen_width - width); + //y = plugin->progress_bar_vertical_alignment * (screen_height - height); + y = calculate_animation_y(view, + plugin->progress_bar_vertical_alignment, + plugin->mode_settings[plugin->mode].animation_vertical_alignment_type, + screen_height, height); + ply_progress_bar_show (view->progress_bar, view->display, + x, y, width, height); + ply_pixel_display_draw_area (view->display, x, y, width, height); + view->animation_bottom = y + height; + } + + if (plugin->mode_settings[plugin->mode].use_animation && + view->throbber != NULL) { + width = ply_throbber_get_width (view->throbber); + height = ply_throbber_get_height (view->throbber); + x = plugin->mode_settings[plugin->mode].animation_horizontal_alignment * screen_width - width / 2.0; + + y = calculate_animation_y(view, + plugin->mode_settings[plugin->mode].animation_vertical_alignment, + plugin->mode_settings[plugin->mode].animation_vertical_alignment_type, + screen_height, height); + ply_throbber_start (view->throbber, + plugin->loop, + view->display, x, y); + ply_pixel_display_draw_area (view->display, x, y, width, height); + view->animation_bottom = y + height; + } + + /* We don't really know how long shutdown will so + * don't show the progress animation + */ + if (plugin->mode == PLY_BOOT_SPLASH_MODE_SHUTDOWN || + plugin->mode == PLY_BOOT_SPLASH_MODE_REBOOT) + return; + + if (plugin->mode_settings[plugin->mode].use_animation && + view->progress_animation != NULL) { + width = ply_progress_animation_get_width (view->progress_animation); + height = ply_progress_animation_get_height (view->progress_animation); + x = plugin->mode_settings[plugin->mode].animation_horizontal_alignment * screen_width - width / 2.0; + y = calculate_animation_y(view, + plugin->mode_settings[plugin->mode].animation_vertical_alignment, + plugin->mode_settings[plugin->mode].animation_vertical_alignment_type, + screen_height, height); + ply_progress_animation_show (view->progress_animation, + view->display, x, y); + + ply_pixel_display_draw_area (view->display, x, y, width, height); + view->animation_bottom = y + height; + } +} + +static void +view_show_prompt (view_t *view, + const char *prompt, + const char *entry_text, + int number_of_bullets) +{ + ply_boot_splash_plugin_t *plugin; + unsigned long screen_width, screen_height, entry_width, entry_height; + unsigned long keyboard_indicator_width, keyboard_indicator_height; + bool show_keyboard_indicators = false; + long dialog_bottom; + int x, y; + + assert (view != NULL); + + plugin = view->plugin; + + screen_width = ply_pixel_display_get_width (view->display); + screen_height = ply_pixel_display_get_height (view->display); + + if (ply_entry_is_hidden (view->entry)) { + view->lock_area.width = ply_image_get_width (plugin->lock_image); + view->lock_area.height = ply_image_get_height (plugin->lock_image); + + entry_width = ply_entry_get_width (view->entry); + entry_height = ply_entry_get_height (view->entry); + + if (plugin->box_image) { + view->box_area.width = ply_image_get_width (plugin->box_image); + view->box_area.height = ply_image_get_height (plugin->box_image); + view->box_area.x = (screen_width - view->box_area.width) * plugin->dialog_horizontal_alignment; + view->box_area.y = (screen_height - view->box_area.height) * plugin->dialog_vertical_alignment; + view->dialog_area = view->box_area; + } else { + view->dialog_area.width = view->lock_area.width + entry_width; + view->dialog_area.height = MAX(view->lock_area.height, entry_height); + view->dialog_area.x = (screen_width - view->dialog_area.width) * plugin->dialog_horizontal_alignment; + view->dialog_area.y = (screen_height - view->dialog_area.height) * plugin->dialog_vertical_alignment; + } + + view->lock_area.x = + view->dialog_area.x + + (view->dialog_area.width - + (view->lock_area.width + entry_width)) / 2.0; + view->lock_area.y = + view->dialog_area.y + + (view->dialog_area.height - view->lock_area.height) / 2.0; + + x = view->lock_area.x + view->lock_area.width; + y = view->dialog_area.y + + (view->dialog_area.height - entry_height) / 2.0; + + ply_entry_show (view->entry, plugin->loop, view->display, x, y); + + show_keyboard_indicators = true; + } + + if (entry_text != NULL) + ply_entry_set_text (view->entry, entry_text); + + if (number_of_bullets != -1) + ply_entry_set_bullet_count (view->entry, number_of_bullets); + + dialog_bottom = view->dialog_area.y + view->dialog_area.height; + + if (prompt != NULL) { + ply_label_set_text (view->label, prompt); + + /* We center the prompt in the middle and use 80% of the horizontal space */ + int label_width = screen_width * 100 / 80; + ply_label_set_alignment (view->label, PLY_LABEL_ALIGN_CENTER); + ply_label_set_width (view->label, label_width); + + x = (screen_width - label_width) / 2; + y = dialog_bottom; + + ply_label_show (view->label, view->display, x, y); + + dialog_bottom += ply_label_get_height (view->label); + } + + if (show_keyboard_indicators) { + keyboard_indicator_width = + ply_keymap_icon_get_width (view->keymap_icon); + keyboard_indicator_height = MAX( + ply_capslock_icon_get_height (view->capslock_icon), + ply_keymap_icon_get_height (view->keymap_icon)); + + x = (screen_width - keyboard_indicator_width) * plugin->dialog_horizontal_alignment; + y = dialog_bottom + keyboard_indicator_height / 2 + + (keyboard_indicator_height - ply_keymap_icon_get_height (view->keymap_icon)) / 2.0; + ply_keymap_icon_show (view->keymap_icon, x, y); + + x += ply_keymap_icon_get_width (view->keymap_icon); + y = dialog_bottom + keyboard_indicator_height / 2 + + (keyboard_indicator_height - ply_capslock_icon_get_height (view->capslock_icon)) / 2.0; + ply_capslock_icon_show (view->capslock_icon, plugin->loop, view->display, x, y); + } +} + +static void +view_hide_prompt (view_t *view) +{ + assert (view != NULL); + + ply_entry_hide (view->entry); + ply_capslock_icon_hide (view->capslock_icon); + ply_keymap_icon_hide (view->keymap_icon); + ply_label_hide (view->label); +} + +static void +load_mode_settings (ply_boot_splash_plugin_t *plugin, + ply_key_file_t *key_file, + const char *group_name, + ply_boot_splash_mode_t mode) +{ + mode_settings_t *settings = &plugin->mode_settings[mode]; + + settings->suppress_messages = + ply_key_file_get_bool (key_file, group_name, "SuppressMessages"); + settings->progress_bar_show_percent_complete = + ply_key_file_get_bool (key_file, group_name, "ProgressBarShowPercentComplete"); + settings->use_progress_bar = + ply_key_file_get_bool (key_file, group_name, "UseProgressBar"); + settings->use_firmware_background = + ply_key_file_get_bool (key_file, group_name, "UseFirmwareBackground"); + + /* This defaults to !use_progress_bar for compat. with older themes */ + if (ply_key_file_has_key (key_file, group_name, "UseAnimation")) + settings->use_animation = + ply_key_file_get_bool (key_file, group_name, "UseAnimation"); + else + settings->use_animation = !settings->use_progress_bar; + + /* This defaults to true for compat. with older themes */ + if (ply_key_file_has_key (key_file, group_name, "UseEndAnimation")) + settings->use_end_animation = + ply_key_file_get_bool (key_file, group_name, "UseEndAnimation"); + else + settings->use_end_animation = true; + + /* If any mode uses the firmware background, then we need to load it */ + if (settings->use_firmware_background) + plugin->use_firmware_background = true; + + settings->watermark_imagename = ply_key_file_get_value (key_file, group_name, "WatermarkImage"); + if( settings->watermark_imagename == NULL && + plugin->watermark_imagename != NULL ) + settings->watermark_imagename = strdup(plugin->watermark_imagename); + + settings->title = ply_key_file_get_value (key_file, group_name, "Title"); + settings->subtitle = ply_key_file_get_value (key_file, group_name, "SubTitle"); + + settings->title_color = ply_key_file_get_long (key_file, group_name, "TitleColor", + plugin->title_color); + + settings->background_start_color = + ply_key_file_get_long (key_file, group_name, + "BackgroundStartColor", + plugin->background_start_color); + settings->background_end_color = + ply_key_file_get_long (key_file, group_name, + "BackgroundEndColor", + plugin->background_end_color); + + /* Throbber, progress- and end-animation alignment */ + settings->animation_horizontal_alignment = + ply_key_file_get_double (key_file, group_name, + "HorizontalAlignment", plugin->animation_horizontal_alignment); + settings->animation_vertical_alignment = + ply_key_file_get_double (key_file, group_name, + "VerticalAlignment", plugin->animation_vertical_alignment); + settings->animation_vertical_alignment_type = + ply_key_file_get_value (key_file, group_name, + "VerticalAlignmentType"); + if( settings->animation_vertical_alignment_type == NULL && + plugin->animation_vertical_alignment_type != NULL ) + settings->animation_vertical_alignment_type = strdup(plugin->animation_vertical_alignment_type); +} + +static ply_boot_splash_plugin_t * +create_plugin (ply_key_file_t *key_file) +{ + ply_boot_splash_plugin_t *plugin; + char *image_dir, *image_path; + char *transition; + char *progress_function; + + srand ((int) ply_get_timestamp ()); + plugin = calloc (1, sizeof(ply_boot_splash_plugin_t)); + + image_dir = ply_key_file_get_value (key_file, "calculate", "ImageDir"); + + ply_trace ("Using '%s' as working directory", image_dir); + + asprintf (&image_path, "%s/lock.png", image_dir); + plugin->lock_image = ply_image_new (image_path); + free (image_path); + + asprintf (&image_path, "%s/box.png", image_dir); + plugin->box_image = ply_image_new (image_path); + free (image_path); + + asprintf (&image_path, "%s/corner-image.png", image_dir); + plugin->corner_image = ply_image_new (image_path); + free (image_path); + + asprintf (&image_path, "%s/header-image.png", image_dir); + plugin->header_image = ply_image_new (image_path); + free (image_path); + + asprintf (&image_path, "%s/background-tile.png", image_dir); + plugin->background_tile_image = ply_image_new (image_path); + free (image_path); + + plugin->animation_dir = image_dir; + + plugin->font = ply_key_file_get_value (key_file, "calculate", "Font"); + plugin->title_font = ply_key_file_get_value (key_file, "calculate", "TitleFont"); + + /* Throbber, progress- and end-animation alignment */ + plugin->animation_horizontal_alignment = + ply_key_file_get_double (key_file, "calculate", + "HorizontalAlignment", 0.5); + plugin->animation_vertical_alignment = + ply_key_file_get_double (key_file, "calculate", + "VerticalAlignment", 0.5); + plugin->animation_vertical_alignment_type = + ply_key_file_get_value (key_file, "calculate", + "VerticalAlignmentType"); + + /* Progressbar alignment, this defaults to the animation alignment + * for compatibility with older themes. + */ + plugin->progress_bar_horizontal_alignment = + ply_key_file_get_double (key_file, "calculate", + "ProgressBarHorizontalAlignment", + plugin->animation_horizontal_alignment); + plugin->progress_bar_vertical_alignment = + ply_key_file_get_double (key_file, "calculate", + "ProgressBarVerticalAlignment", + plugin->animation_vertical_alignment); + + /* Watermark alignment */ + plugin->watermark_horizontal_alignment = + ply_key_file_get_double (key_file, "calculate", + "WatermarkHorizontalAlignment", 1.0); + plugin->watermark_vertical_alignment = + ply_key_file_get_double (key_file, "calculate", + "WatermarkVerticalAlignment", 0.5); + + /* Password (or other) dialog alignment */ + plugin->dialog_horizontal_alignment = + ply_key_file_get_double (key_file, "calculate", + "DialogHorizontalAlignment", 0.5); + plugin->dialog_vertical_alignment = + ply_key_file_get_double (key_file, "calculate", + "DialogVerticalAlignment", 0.5); + + /* Title alignment */ + plugin->title_horizontal_alignment = + ply_key_file_get_double (key_file, "calculate", + "TitleHorizontalAlignment", 0.5); + plugin->title_vertical_alignment = + ply_key_file_get_double (key_file, "calculate", + "TitleVerticalAlignment", 0.5); + + plugin->transition = PLY_PROGRESS_ANIMATION_TRANSITION_NONE; + transition = ply_key_file_get_value (key_file, "calculate", "Transition"); + if (transition != NULL) { + if (strcmp (transition, "fade-over") == 0) + plugin->transition = PLY_PROGRESS_ANIMATION_TRANSITION_FADE_OVER; + else if (strcmp (transition, "cross-fade") == 0) + plugin->transition = PLY_PROGRESS_ANIMATION_TRANSITION_CROSS_FADE; + else if (strcmp (transition, "merge-fade") == 0) + plugin->transition = PLY_PROGRESS_ANIMATION_TRANSITION_MERGE_FADE; + } + free (transition); + + plugin->transition_duration = + ply_key_file_get_double (key_file, "calculate", + "TransitionDuration", 0.0); + + plugin->background_start_color = + ply_key_file_get_long (key_file, "calculate", + "BackgroundStartColor", + PLYMOUTH_BACKGROUND_START_COLOR); + plugin->background_end_color = + ply_key_file_get_long (key_file, "calculate", + "BackgroundEndColor", + PLYMOUTH_BACKGROUND_END_COLOR); + plugin->title_color = ply_key_file_get_long ( + key_file, "calculate", "TitleColor", 0xffffff); + + plugin->progress_bar_bg_color = + ply_key_file_get_long (key_file, "calculate", + "ProgressBarBackgroundColor", + 0xffffff /* white */); + plugin->progress_bar_fg_color = + ply_key_file_get_long (key_file, "calculate", + "ProgressBarForegroundColor", + 0x000000 /* black */); + plugin->progress_bar_width = + ply_key_file_get_long (key_file, "calculate", + "ProgressBarWidth", + PROGRESS_BAR_WIDTH); + plugin->progress_bar_height = + ply_key_file_get_long (key_file, "calculate", + "ProgressBarHeight", + PROGRESS_BAR_HEIGHT); + + plugin->watermark_imagename = ply_key_file_get_value (key_file, "calculate", "WatermarkImage"); + + load_mode_settings (plugin, key_file, "boot-up", PLY_BOOT_SPLASH_MODE_BOOT_UP); + load_mode_settings (plugin, key_file, "shutdown", PLY_BOOT_SPLASH_MODE_SHUTDOWN); + load_mode_settings (plugin, key_file, "reboot", PLY_BOOT_SPLASH_MODE_REBOOT); + load_mode_settings (plugin, key_file, "updates", PLY_BOOT_SPLASH_MODE_UPDATES); + load_mode_settings (plugin, key_file, "system-upgrade", PLY_BOOT_SPLASH_MODE_SYSTEM_UPGRADE); + load_mode_settings (plugin, key_file, "firmware-upgrade", PLY_BOOT_SPLASH_MODE_FIRMWARE_UPGRADE); + + if (plugin->use_firmware_background) + plugin->background_bgrt_image = ply_image_new ("/sys/firmware/acpi/bgrt/image"); + + plugin->dialog_clears_firmware_background = + ply_key_file_get_bool (key_file, "calculate", "DialogClearsFirmwareBackground"); + + plugin->message_below_animation = + ply_key_file_get_bool (key_file, "calculate", "MessageBelowAnimation"); + + progress_function = ply_key_file_get_value (key_file, "calculate", "ProgressFunction"); + + if (progress_function != NULL) { + if (strcmp (progress_function, "wwoods") == 0) { + ply_trace ("Using wwoods progress function"); + plugin->progress_function = PROGRESS_FUNCTION_TYPE_WWOODS; + } else if (strcmp (progress_function, "linear") == 0) { + ply_trace ("Using linear progress function"); + plugin->progress_function = PROGRESS_FUNCTION_TYPE_LINEAR; + } else { + ply_trace ("unknown progress function %s, defaulting to linear", progress_function); + plugin->progress_function = PROGRESS_FUNCTION_TYPE_LINEAR; + } + + free (progress_function); + } + + plugin->views = ply_list_new (); + + return plugin; +} + +static void +free_views (ply_boot_splash_plugin_t *plugin) +{ + ply_list_node_t *node; + + ply_trace ("freeing views"); + + node = ply_list_get_first_node (plugin->views); + + while (node != NULL) { + ply_list_node_t *next_node; + view_t *view; + + view = ply_list_node_get_data (node); + next_node = ply_list_get_next_node (plugin->views, node); + + view_free (view); + ply_list_remove_node (plugin->views, node); + + node = next_node; + } + + ply_list_free (plugin->views); + plugin->views = NULL; +} + +static void +destroy_plugin (ply_boot_splash_plugin_t *plugin) +{ + int i; + + if (plugin == NULL) + return; + + ply_trace ("destroying plugin"); + + if (plugin->loop != NULL) { + stop_animation (plugin); + + ply_event_loop_stop_watching_for_exit (plugin->loop, (ply_event_loop_exit_handler_t) + detach_from_event_loop, + plugin); + detach_from_event_loop (plugin); + } + + ply_image_free (plugin->lock_image); + + if (plugin->box_image != NULL) + ply_image_free (plugin->box_image); + + if (plugin->corner_image != NULL) + ply_image_free (plugin->corner_image); + + if (plugin->header_image != NULL) + ply_image_free (plugin->header_image); + + if (plugin->background_tile_image != NULL) + ply_image_free (plugin->background_tile_image); + + if (plugin->background_bgrt_image != NULL) + ply_image_free (plugin->background_bgrt_image); + + for (i = 0; i < PLY_BOOT_SPLASH_MODE_COUNT; i++) { + free (plugin->mode_settings[i].title); + free (plugin->mode_settings[i].watermark_imagename); + free (plugin->mode_settings[i].subtitle); + free (plugin->mode_settings[i].animation_vertical_alignment_type); + } + + free (plugin->font); + free (plugin->title_font); + free (plugin->animation_vertical_alignment_type); + free (plugin->animation_dir); + free (plugin->watermark_imagename); + free_views (plugin); + free (plugin); +} + +static void +start_end_animation (ply_boot_splash_plugin_t *plugin, + ply_trigger_t *trigger) +{ + ply_list_node_t *node; + view_t *view; + + if (!plugin->mode_settings[plugin->mode].use_animation) { + ply_trigger_pull (trigger, NULL); + return; + } + + if (!plugin->mode_settings[plugin->mode].use_end_animation) { + node = ply_list_get_first_node (plugin->views); + while (node != NULL) { + view = ply_list_node_get_data (node); + + ply_progress_bar_hide (view->progress_bar); + + if (view->throbber != NULL) + ply_throbber_stop (view->throbber, NULL); + + if (view->progress_animation != NULL) + ply_progress_animation_hide (view->progress_animation); + + node = ply_list_get_next_node (plugin->views, node); + } + ply_trigger_pull (trigger, NULL); + return; + } + + ply_trace ("starting end animation"); + + node = ply_list_get_first_node (plugin->views); + while (node != NULL) { + view = ply_list_node_get_data (node); + + ply_trigger_ignore_next_pull (trigger); + + if (view->throbber != NULL) { + ply_trigger_t *throbber_trigger; + ply_trace ("stopping throbber"); + view->end_trigger = trigger; + throbber_trigger = ply_trigger_new (NULL); + ply_trigger_add_handler (throbber_trigger, + (ply_trigger_handler_t) + on_view_throbber_stopped, + view); + ply_throbber_stop (view->throbber, throbber_trigger); + } else { + view_start_end_animation (view, trigger); + } + + node = ply_list_get_next_node (plugin->views, node); + } + ply_trigger_pull (trigger, NULL); +} + +static void +start_progress_animation (ply_boot_splash_plugin_t *plugin) +{ + ply_list_node_t *node; + view_t *view; + + if (plugin->is_animating) + return; + + ply_trace ("starting animation"); + + node = ply_list_get_first_node (plugin->views); + while (node != NULL) { + view = ply_list_node_get_data (node); + view_start_progress_animation (view); + node = ply_list_get_next_node (plugin->views, node); + } + + plugin->is_animating = true; + + /* We don't really know how long shutdown will, take + * but it's normally really fast, so just jump to + * the end animation + */ + if (plugin->mode_settings[plugin->mode].use_end_animation && + (plugin->mode == PLY_BOOT_SPLASH_MODE_SHUTDOWN || + plugin->mode == PLY_BOOT_SPLASH_MODE_REBOOT)) + become_idle (plugin, NULL); +} + +static void +stop_animation (ply_boot_splash_plugin_t *plugin) +{ + ply_list_node_t *node; + view_t *view; + + assert (plugin != NULL); + assert (plugin->loop != NULL); + + if (!plugin->is_animating) + return; + + ply_trace ("stopping animation"); + plugin->is_animating = false; + + node = ply_list_get_first_node (plugin->views); + while (node != NULL) { + view = ply_list_node_get_data (node); + + ply_progress_bar_hide (view->progress_bar); + if (view->progress_animation != NULL) + ply_progress_animation_hide (view->progress_animation); + if (view->throbber != NULL) + ply_throbber_stop (view->throbber, NULL); + if (view->end_animation != NULL) + ply_animation_stop (view->end_animation); + + node = ply_list_get_next_node (plugin->views, node); + } +} + +static void +detach_from_event_loop (ply_boot_splash_plugin_t *plugin) +{ + plugin->loop = NULL; +} + +static void +draw_background (view_t *view, + ply_pixel_buffer_t *pixel_buffer, + int x, + int y, + int width, + int height) +{ + ply_boot_splash_plugin_t *plugin; + ply_rectangle_t area; + bool use_black_background = false; + + plugin = view->plugin; + + area.x = x; + area.y = y; + area.width = width; + area.height = height; + + /* When using the firmware logo as background and we should not use + * it for this mode, use solid black as background. + */ + if (plugin->background_bgrt_image && + !plugin->mode_settings[plugin->mode].use_firmware_background) + use_black_background = true; + + /* When using the firmware logo as background, use solid black as + * background for dialogs. + */ + if ((plugin->state == PLY_BOOT_SPLASH_DISPLAY_QUESTION_ENTRY || + plugin->state == PLY_BOOT_SPLASH_DISPLAY_PASSWORD_ENTRY) && + plugin->background_bgrt_image && plugin->dialog_clears_firmware_background) + use_black_background = true; + + if (use_black_background) + ply_pixel_buffer_fill_with_hex_color (pixel_buffer, &area, 0); + else if (view->background_buffer != NULL) + ply_pixel_buffer_fill_with_buffer (pixel_buffer, view->background_buffer, 0, 0); + else if (plugin->mode_settings[plugin->mode].background_start_color != plugin->mode_settings[plugin->mode].background_end_color) + ply_pixel_buffer_fill_with_gradient (pixel_buffer, &area, + plugin->mode_settings[plugin->mode].background_start_color, + plugin->mode_settings[plugin->mode].background_end_color); + else + ply_pixel_buffer_fill_with_hex_color (pixel_buffer, &area, + plugin->mode_settings[plugin->mode].background_start_color); + + if (view->watermark_image != NULL) { + uint32_t *data; + + data = ply_image_get_data (view->watermark_image); + ply_pixel_buffer_fill_with_argb32_data (pixel_buffer, &view->watermark_area, data); + } +} + +static void +on_draw (view_t *view, + ply_pixel_buffer_t *pixel_buffer, + int x, + int y, + int width, + int height) +{ + ply_boot_splash_plugin_t *plugin; + ply_rectangle_t screen_area; + ply_rectangle_t image_area; + + plugin = view->plugin; + + draw_background (view, pixel_buffer, x, y, width, height); + + ply_pixel_buffer_get_size (pixel_buffer, &screen_area); + + if (plugin->state == PLY_BOOT_SPLASH_DISPLAY_QUESTION_ENTRY || + plugin->state == PLY_BOOT_SPLASH_DISPLAY_PASSWORD_ENTRY) { + uint32_t *box_data, *lock_data; + + if (plugin->box_image) { + box_data = ply_image_get_data (plugin->box_image); + ply_pixel_buffer_fill_with_argb32_data (pixel_buffer, + &view->box_area, + box_data); + } + + ply_entry_draw_area (view->entry, + pixel_buffer, + x, y, width, height); + ply_keymap_icon_draw_area (view->keymap_icon, + pixel_buffer, + x, y, width, height); + ply_capslock_icon_draw_area (view->capslock_icon, + pixel_buffer, + x, y, width, height); + ply_label_draw_area (view->label, + pixel_buffer, + x, y, width, height); + + lock_data = ply_image_get_data (plugin->lock_image); + ply_pixel_buffer_fill_with_argb32_data (pixel_buffer, + &view->lock_area, + lock_data); + } else { + if (plugin->mode_settings[plugin->mode].use_progress_bar) + ply_progress_bar_draw_area (view->progress_bar, pixel_buffer, + x, y, width, height); + + if (plugin->mode_settings[plugin->mode].use_animation && + view->throbber != NULL) + ply_throbber_draw_area (view->throbber, pixel_buffer, + x, y, width, height); + + if (plugin->mode_settings[plugin->mode].use_animation && + view->progress_animation != NULL) + ply_progress_animation_draw_area (view->progress_animation, + pixel_buffer, + x, y, width, height); + + if (plugin->mode_settings[plugin->mode].use_animation && + view->end_animation != NULL) + ply_animation_draw_area (view->end_animation, + pixel_buffer, + x, y, width, height); + + if (plugin->corner_image != NULL) { + image_area.width = ply_image_get_width (plugin->corner_image); + image_area.height = ply_image_get_height (plugin->corner_image); + image_area.x = screen_area.width - image_area.width - 20; + image_area.y = screen_area.height - image_area.height - 20; + + ply_pixel_buffer_fill_with_argb32_data (pixel_buffer, &image_area, ply_image_get_data (plugin->corner_image)); + } + + if (plugin->header_image != NULL) { + long sprite_height; + + + if (view->progress_animation != NULL) + sprite_height = ply_progress_animation_get_height (view->progress_animation); + else + sprite_height = 0; + + if (view->throbber != NULL) + sprite_height = MAX (ply_throbber_get_height (view->throbber), + sprite_height); + + image_area.width = ply_image_get_width (plugin->header_image); + image_area.height = ply_image_get_height (plugin->header_image); + image_area.x = screen_area.width / 2.0 - image_area.width / 2.0; + image_area.y = plugin->mode_settings[plugin->mode].animation_vertical_alignment * screen_area.height - sprite_height / 2.0 - image_area.height; + + + ply_pixel_buffer_fill_with_argb32_data (pixel_buffer, &image_area, ply_image_get_data (plugin->header_image)); + } + ply_label_draw_area (view->title_label, + pixel_buffer, + x, y, width, height); + ply_label_draw_area (view->subtitle_label, + pixel_buffer, + x, y, width, height); + } + ply_label_draw_area (view->message_label, + pixel_buffer, + x, y, width, height); +} + +static void +add_pixel_display (ply_boot_splash_plugin_t *plugin, + ply_pixel_display_t *display) +{ + view_t *view; + + ply_trace ("adding pixel display to plugin"); + view = view_new (plugin, display); + + ply_pixel_display_set_draw_handler (view->display, + (ply_pixel_display_draw_handler_t) + on_draw, view); + if (plugin->is_visible) { + if (view_load (view)) { + ply_list_append_data (plugin->views, view); + if (plugin->is_animating) + view_start_progress_animation (view); + } else { + view_free (view); + } + } else { + ply_list_append_data (plugin->views, view); + } +} + +static void +remove_pixel_display (ply_boot_splash_plugin_t *plugin, + ply_pixel_display_t *display) +{ + ply_list_node_t *node; + + ply_trace ("removing pixel display from plugin"); + node = ply_list_get_first_node (plugin->views); + while (node != NULL) { + view_t *view; + ply_list_node_t *next_node; + + view = ply_list_node_get_data (node); + next_node = ply_list_get_next_node (plugin->views, node); + + if (view->display == display) { + ply_pixel_display_set_draw_handler (view->display, NULL, NULL); + view_free (view); + ply_list_remove_node (plugin->views, node); + return; + } + + node = next_node; + } +} + +static bool +show_splash_screen (ply_boot_splash_plugin_t *plugin, + ply_event_loop_t *loop, + ply_buffer_t *boot_buffer, + ply_boot_splash_mode_t mode) +{ + int i; + + assert (plugin != NULL); + + plugin->loop = loop; + plugin->mode = mode; + + ply_trace ("loading lock image"); + if (!ply_image_load (plugin->lock_image)) + return false; + + if (plugin->box_image != NULL) { + ply_trace ("loading box image"); + + if (!ply_image_load (plugin->box_image)) { + ply_image_free (plugin->box_image); + plugin->box_image = NULL; + } + } + + if (plugin->corner_image != NULL) { + ply_trace ("loading corner image"); + + if (!ply_image_load (plugin->corner_image)) { + ply_image_free (plugin->corner_image); + plugin->corner_image = NULL; + } + } + + if (plugin->header_image != NULL) { + ply_trace ("loading header image"); + + if (!ply_image_load (plugin->header_image)) { + ply_image_free (plugin->header_image); + plugin->header_image = NULL; + } + } + + if (plugin->background_tile_image != NULL) { + ply_trace ("loading background tile image"); + if (!ply_image_load (plugin->background_tile_image)) { + ply_image_free (plugin->background_tile_image); + plugin->background_tile_image = NULL; + } + } + + if (plugin->background_bgrt_image != NULL) { + ply_trace ("loading background bgrt image"); + if (ply_image_load (plugin->background_bgrt_image)) { + plugin->background_bgrt_raw_width = ply_image_get_width (plugin->background_bgrt_image); + plugin->background_bgrt_raw_height = ply_image_get_height (plugin->background_bgrt_image); + } else { + ply_image_free (plugin->background_bgrt_image); + plugin->background_bgrt_image = NULL; + for (i = 0; i < PLY_BOOT_SPLASH_MODE_COUNT; i++) + plugin->mode_settings[i].use_firmware_background = false; + plugin->use_firmware_background = false; + } + } + + if (!load_views (plugin)) { + ply_trace ("couldn't load views"); + return false; + } + + ply_event_loop_watch_for_exit (loop, (ply_event_loop_exit_handler_t) + detach_from_event_loop, + plugin); + + ply_trace ("starting boot animations"); + start_progress_animation (plugin); + + plugin->is_visible = true; + + return true; +} + +static void +update_status (ply_boot_splash_plugin_t *plugin, + const char *status) +{ + assert (plugin != NULL); +} + +static void +on_animation_stopped (ply_boot_splash_plugin_t *plugin) +{ + if (plugin->idle_trigger != NULL) { + ply_trigger_pull (plugin->idle_trigger, NULL); + plugin->idle_trigger = NULL; + } + plugin->is_idle = true; +} + +static void +update_progress_animation (ply_boot_splash_plugin_t *plugin, + double fraction_done) +{ + ply_list_node_t *node; + view_t *view; + char buf[64]; + + node = ply_list_get_first_node (plugin->views); + while (node != NULL) { + view = ply_list_node_get_data (node); + + if (view->progress_animation != NULL) + ply_progress_animation_set_fraction_done (view->progress_animation, + fraction_done); + + ply_progress_bar_set_fraction_done (view->progress_bar, fraction_done); + if (!ply_progress_bar_is_hidden (view->progress_bar) && + plugin->mode_settings[plugin->mode].progress_bar_show_percent_complete) { + snprintf (buf, sizeof(buf), _("%d%% complete"), (int)(fraction_done * 100)); + view_show_message (view, buf); + } + + node = ply_list_get_next_node (plugin->views, node); + } +} + +static void +on_boot_progress (ply_boot_splash_plugin_t *plugin, + double duration, + double fraction_done) +{ + if (plugin->mode == PLY_BOOT_SPLASH_MODE_UPDATES || + plugin->mode == PLY_BOOT_SPLASH_MODE_SYSTEM_UPGRADE || + plugin->mode == PLY_BOOT_SPLASH_MODE_FIRMWARE_UPGRADE) + return; + + if (plugin->state != PLY_BOOT_SPLASH_DISPLAY_NORMAL) + return; + + if (plugin->is_idle) + return; + + /* + * If we do not have an end animation, we keep showing progress until + * become_idle gets called. + */ + if (plugin->mode_settings[plugin->mode].use_end_animation && + fraction_done >= SHOW_ANIMATION_FRACTION) { + if (plugin->stop_trigger == NULL) { + ply_trace ("boot progressed to end"); + + plugin->stop_trigger = ply_trigger_new (&plugin->stop_trigger); + ply_trigger_add_handler (plugin->stop_trigger, + (ply_trigger_handler_t) + on_animation_stopped, + plugin); + start_end_animation (plugin, plugin->stop_trigger); + } + } else { + double total_duration; + + fraction_done *= (1 / SHOW_ANIMATION_FRACTION); + + switch (plugin->progress_function) { + /* Fun made-up smoothing function to make the growth asymptotic: + * fraction(time,estimate)=1-2^(-(time^1.45)/estimate) */ + case PROGRESS_FUNCTION_TYPE_WWOODS: + total_duration = duration / fraction_done; + fraction_done = 1.0 - pow (2.0, -pow (duration, 1.45) / total_duration) * (1.0 - fraction_done); + break; + + case PROGRESS_FUNCTION_TYPE_LINEAR: + break; + } + + update_progress_animation (plugin, fraction_done); + } +} + +static void +hide_splash_screen (ply_boot_splash_plugin_t *plugin, + ply_event_loop_t *loop) +{ + assert (plugin != NULL); + + ply_trace ("hiding splash"); + if (plugin->loop != NULL) { + stop_animation (plugin); + + ply_event_loop_stop_watching_for_exit (plugin->loop, (ply_event_loop_exit_handler_t) + detach_from_event_loop, + plugin); + detach_from_event_loop (plugin); + } + + plugin->is_visible = false; +} + +static void +show_prompt (ply_boot_splash_plugin_t *plugin, + const char *prompt, + const char *entry_text, + int number_of_bullets) +{ + ply_list_node_t *node; + view_t *view; + + ply_trace ("showing prompt"); + node = ply_list_get_first_node (plugin->views); + while (node != NULL) { + view = ply_list_node_get_data (node); + view_show_prompt (view, prompt, entry_text, number_of_bullets); + node = ply_list_get_next_node (plugin->views, node); + } +} + +static void +on_root_mounted (ply_boot_splash_plugin_t *plugin) +{ + ply_trace ("root filesystem mounted"); + plugin->root_is_mounted = true; +} + +static void +become_idle (ply_boot_splash_plugin_t *plugin, + ply_trigger_t *idle_trigger) +{ + ply_trace ("deactivation requested"); + if (plugin->is_idle) { + ply_trace ("plugin is already idle"); + ply_trigger_pull (idle_trigger, NULL); + return; + } + + plugin->idle_trigger = idle_trigger; + + if (plugin->stop_trigger == NULL) { + ply_trace ("waiting for plugin to stop"); + plugin->stop_trigger = ply_trigger_new (&plugin->stop_trigger); + ply_trigger_add_handler (plugin->stop_trigger, + (ply_trigger_handler_t) + on_animation_stopped, + plugin); + start_end_animation (plugin, plugin->stop_trigger); + } else { + ply_trace ("already waiting for plugin to stop"); + } +} + +static void +hide_prompt (ply_boot_splash_plugin_t *plugin) +{ + ply_list_node_t *node; + view_t *view; + + ply_trace ("hiding prompt"); + node = ply_list_get_first_node (plugin->views); + while (node != NULL) { + view = ply_list_node_get_data (node); + view_hide_prompt (view); + node = ply_list_get_next_node (plugin->views, node); + } +} + +static void +view_show_message (view_t *view, + const char *message) +{ + ply_boot_splash_plugin_t *plugin = view->plugin; + int x, y, width, height; + + if (plugin->message_below_animation) + ply_label_set_alignment (view->message_label, PLY_LABEL_ALIGN_CENTER); + + ply_label_set_text (view->message_label, message); + width = ply_label_get_width (view->message_label); + height = ply_label_get_height (view->message_label); + + if (plugin->message_below_animation) { + x = (ply_pixel_display_get_width (view->display) - width) * 0.5; + y = view->animation_bottom + 10; + } else { + x = 10; + y = 10; + } + + ply_label_show (view->message_label, view->display, x, y); + ply_pixel_display_draw_area (view->display, x, y, width, height); +} + +static void +show_message (ply_boot_splash_plugin_t *plugin, + const char *message) +{ + ply_list_node_t *node; + view_t *view; + + if (plugin->mode_settings[plugin->mode].suppress_messages) { + ply_trace ("Suppressing message '%s'", message); + return; + } + ply_trace ("Showing message '%s'", message); + node = ply_list_get_first_node (plugin->views); + while (node != NULL) { + view = ply_list_node_get_data (node); + view_show_message (view, message); + node = ply_list_get_next_node (plugin->views, node); + } +} + +static void +system_update (ply_boot_splash_plugin_t *plugin, + int progress) +{ + if (plugin->mode != PLY_BOOT_SPLASH_MODE_UPDATES && + plugin->mode != PLY_BOOT_SPLASH_MODE_SYSTEM_UPGRADE && + plugin->mode != PLY_BOOT_SPLASH_MODE_FIRMWARE_UPGRADE) + return; + + update_progress_animation (plugin, progress / 100.0); +} + +static void +display_normal (ply_boot_splash_plugin_t *plugin) +{ + pause_views (plugin); + if (plugin->state != PLY_BOOT_SPLASH_DISPLAY_NORMAL) + hide_prompt (plugin); + + plugin->state = PLY_BOOT_SPLASH_DISPLAY_NORMAL; + start_progress_animation (plugin); + redraw_views (plugin); + unpause_views (plugin); +} + +static void +display_password (ply_boot_splash_plugin_t *plugin, + const char *prompt, + int bullets) +{ + pause_views (plugin); + if (plugin->state == PLY_BOOT_SPLASH_DISPLAY_NORMAL) + stop_animation (plugin); + + plugin->state = PLY_BOOT_SPLASH_DISPLAY_PASSWORD_ENTRY; + show_prompt (plugin, prompt, NULL, bullets); + redraw_views (plugin); + unpause_views (plugin); +} + +static void +display_question (ply_boot_splash_plugin_t *plugin, + const char *prompt, + const char *entry_text) +{ + pause_views (plugin); + if (plugin->state == PLY_BOOT_SPLASH_DISPLAY_NORMAL) + stop_animation (plugin); + + plugin->state = PLY_BOOT_SPLASH_DISPLAY_QUESTION_ENTRY; + show_prompt (plugin, prompt, entry_text, -1); + redraw_views (plugin); + unpause_views (plugin); +} + +static void +display_message (ply_boot_splash_plugin_t *plugin, + const char *message) +{ + show_message (plugin, message); +} + +ply_boot_splash_plugin_interface_t * +ply_boot_splash_plugin_get_interface (void) +{ + static ply_boot_splash_plugin_interface_t plugin_interface = + { + .create_plugin = create_plugin, + .destroy_plugin = destroy_plugin, + .add_pixel_display = add_pixel_display, + .remove_pixel_display = remove_pixel_display, + .show_splash_screen = show_splash_screen, + .update_status = update_status, + .on_boot_progress = on_boot_progress, + .hide_splash_screen = hide_splash_screen, + .on_root_mounted = on_root_mounted, + .become_idle = become_idle, + .display_normal = display_normal, + .display_password = display_password, + .display_question = display_question, + .display_message = display_message, + .system_update = system_update, + }; + + return &plugin_interface; +} + +/* vim: set ts=4 sw=4 expandtab autoindent cindent cino={.5s,(0: */ diff --git a/sys-boot/plymouth-calculate-plugin/plymouth-calculate-plugin-0.9.5.ebuild b/sys-boot/plymouth-calculate-plugin/plymouth-calculate-plugin-0.9.5.ebuild new file mode 100644 index 000000000..5ac8a4c87 --- /dev/null +++ b/sys-boot/plymouth-calculate-plugin/plymouth-calculate-plugin-0.9.5.ebuild @@ -0,0 +1,98 @@ +# Copyright 1999-2018 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +EAPI=7 + +inherit flag-o-matic + +MY_P=plymouth-${PV} + +if [[ ${PV} == 9999 ]]; then + inherit git-r3 + EGIT_REPO_URI="https://anongit.freedesktop.org/git/plymouth" +else + SRC_URI="${SRC_URI} https://www.freedesktop.org/software/plymouth/releases/${MY_P}.tar.xz" + KEYWORDS="~alpha amd64 ~arm ~ia64 ~ppc ~ppc64 ~sparc x86" +fi + +inherit autotools readme.gentoo-r1 systemd toolchain-funcs + +DESCRIPTION="Calculate plugin for plymouth" +HOMEPAGE="https://www.calculate-linux.org" + +LICENSE="GPL-2" +SLOT="0" +IUSE="debug gdm +gtk +libkms +pango +split-usr static-libs +udev" + +CDEPEND=" + ~sys-boot/${MY_P}[debug?,udev?,gtk?,libkms?,pango?,split-usr?,static-libs?] +" +DEPEND="${CDEPEND} + app-text/docbook-xsl-stylesheets + dev-libs/libxslt + virtual/pkgconfig +" +RDEPEND="${CDEPEND} + virtual/udev + !