-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Update Plymouth theme to enable smoother progress bar #3661
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,116 +1,105 @@ | ||
| # Omarchy Plymouth Theme Script | ||
|
|
||
| Window.SetBackgroundTopColor(0.101, 0.105, 0.149); | ||
| Window.SetBackgroundBottomColor(0.101, 0.105, 0.149); | ||
| Window.SetBackgroundBottomColor(0.101, 0.105, 0.149); | ||
|
|
||
| logo.image = Image("logo.png"); | ||
| logo.sprite = Sprite(logo.image); | ||
| logo.sprite.SetX (Window.GetWidth() / 2 - logo.image.GetWidth() / 2); | ||
| logo.sprite.SetY (Window.GetHeight() / 2 - logo.image.GetHeight() / 2); | ||
| logo.sprite.SetOpacity (1); | ||
| logo.sprite.SetX(Window.GetWidth() / 2 - logo.image.GetWidth() / 2); | ||
| logo.sprite.SetY(Window.GetHeight() / 2 - logo.image.GetHeight() / 2); | ||
| logo.sprite.SetOpacity(1); | ||
|
|
||
| # Use these to adjust the progress bar timing | ||
| global.fake_progress_limit = 0.7; # Target percentage for fake progress (0.0 to 1.0) | ||
| global.fake_progress_duration = 15.0; # Duration in seconds to reach limit | ||
|
|
||
| # Progress bar animation variables | ||
| global.animation_frame = 0; | ||
| global.fake_progress = 0.0; | ||
| global.real_progress = 0.0; | ||
| global.fake_progress_active = 0; # 0 / 1 boolean | ||
| global.animation_frame = 0; | ||
| global.fake_progress_start_time = 0; # Track when fake progress started | ||
| global.fake_progress_active = 0; | ||
| global.fake_progress_start_time = 0.0; # Track when fake progress started | ||
| global.password_shown = 0; # Track if password dialog has been shown | ||
| global.max_progress = 0.0; # Track the maximum progress reached to prevent backwards movement | ||
|
|
||
| fun refresh_callback () | ||
| { | ||
| global.animation_frame++; | ||
|
|
||
| # Animate fake progress to limit over time with easing | ||
| if (global.fake_progress_active == 1) | ||
| { | ||
| # Calculate elapsed time since start | ||
| elapsed_time = global.animation_frame / 50.0; # Convert frames to seconds (50 FPS) | ||
|
|
||
| # Calculate linear progress ratio (0 to 1) based on time | ||
| time_ratio = elapsed_time / global.fake_progress_duration; | ||
| if (time_ratio > 1.0) | ||
| time_ratio = 1.0; | ||
|
|
||
| # Apply easing curve: ease-out quadratic | ||
| # Formula: 1 - (1 - x)^2 | ||
| eased_ratio = 1 - ((1 - time_ratio) * (1 - time_ratio)); | ||
|
|
||
| # Calculate fake progress based on eased ratio | ||
| global.fake_progress = eased_ratio * global.fake_progress_limit; | ||
|
|
||
| # Update progress bar with fake progress | ||
| update_progress_bar(global.fake_progress); | ||
| } | ||
| } | ||
| fun refresh_callback() { | ||
| global.animation_frame++; | ||
|
|
||
| # Animate fake progress to limit over time with easing | ||
| if (global.fake_progress_active == 1) { | ||
| # Calculate elapsed time since start | ||
| elapsed_time = global.animation_frame / 50.0; # Convert frames to seconds (50 FPS) | ||
|
|
||
| Plymouth.SetRefreshFunction (refresh_callback); | ||
| # Calculate linear progress ratio (0 to 1) based on time | ||
| time_ratio = elapsed_time / global.fake_progress_duration; | ||
| if (time_ratio > 1.0) time_ratio = 1.0; | ||
|
|
||
| #----------------------------------------- Helper Functions -------------------------------- | ||
| # Apply easing curve: ease-out quadratic | ||
| # Formula: 1 - (1 - x)^2 | ||
| eased_ratio = 1 - ((1 - time_ratio) * (1 - time_ratio)); | ||
|
|
||
| fun update_progress_bar(progress) | ||
| { | ||
| # Only update if progress is moving forward | ||
| if (progress > global.max_progress) | ||
| { | ||
| global.max_progress = progress; | ||
| width = Math.Int(progress_bar.original_image.GetWidth() * progress); | ||
| if (width < 1) width = 1; # Ensure minimum width of 1 pixel | ||
|
|
||
| progress_bar.image = progress_bar.original_image.Scale(width, progress_bar.original_image.GetHeight()); | ||
| progress_bar.sprite.SetImage(progress_bar.image); | ||
| } | ||
| } | ||
| # Calculate fake progress based on eased ratio | ||
| global.fake_progress = eased_ratio * global.fake_progress_limit; | ||
|
|
||
| fun show_progress_bar() | ||
| { | ||
| progress_box.sprite.SetOpacity(1); | ||
| progress_bar.sprite.SetOpacity(1); | ||
| # Update progress bar with fake progress | ||
| update_progress_bar(global.fake_progress); | ||
| } | ||
| } | ||
|
|
||
| fun hide_progress_bar() | ||
| { | ||
| progress_box.sprite.SetOpacity(0); | ||
| progress_bar.sprite.SetOpacity(0); | ||
| } | ||
| Plymouth.SetRefreshFunction(refresh_callback); | ||
|
|
||
| fun show_password_dialog() | ||
| { | ||
| lock.sprite.SetOpacity(1); | ||
| entry.sprite.SetOpacity(1); | ||
| } | ||
| #----------------------------------------- Helper Functions -------------------------------- | ||
|
|
||
| fun hide_password_dialog() | ||
| { | ||
| lock.sprite.SetOpacity(0); | ||
| entry.sprite.SetOpacity(0); | ||
| for (index = 0; bullet.sprites[index]; index++) | ||
| bullet.sprites[index].SetOpacity(0); | ||
| } | ||
| fun update_progress_bar(progress) { | ||
| # Only update if progress is moving forward | ||
| if (progress > global.max_progress) { | ||
| global.max_progress = progress; | ||
|
|
||
| width = Math.Int(progress_bar.original_image.GetWidth() * progress); | ||
| if (width < 1) width = 1; # Ensure minimum width of 1 pixel | ||
|
|
||
| fun start_fake_progress() | ||
| { | ||
| # Don't reset if we already have progress | ||
| if (global.max_progress == 0.0) | ||
| { | ||
| global.fake_progress = 0.0; | ||
| global.real_progress = 0.0; | ||
| update_progress_bar(0.0); | ||
| } | ||
| global.fake_progress_active = 1; | ||
| global.animation_frame = 0; | ||
| progress_bar.image = progress_bar.original_image.Scale(width, progress_bar.original_image.GetHeight()); | ||
| progress_bar.sprite.SetImage(progress_bar.image); | ||
| } | ||
| } | ||
|
|
||
| fun show_progress_bar() { | ||
| progress_box.sprite.SetOpacity(1); | ||
| progress_bar.sprite.SetOpacity(1); | ||
| } | ||
|
|
||
| fun hide_progress_bar() { | ||
| progress_box.sprite.SetOpacity(0); | ||
| progress_bar.sprite.SetOpacity(0); | ||
| } | ||
|
|
||
| fun stop_fake_progress() | ||
| { | ||
| global.fake_progress_active = 0; | ||
| fun show_password_dialog() { | ||
| lock.sprite.SetOpacity(1); | ||
| entry.sprite.SetOpacity(1); | ||
| } | ||
|
|
||
| fun hide_password_dialog() { | ||
| lock.sprite.SetOpacity(0); | ||
| entry.sprite.SetOpacity(0); | ||
|
|
||
| for (index = 0; bullet.sprites[index]; index++) { | ||
| bullet.sprites[index].SetOpacity(0); | ||
| } | ||
| } | ||
|
|
||
| fun start_fake_progress() { | ||
| global.fake_progress_active = 1; | ||
|
|
||
| # Reset fake progress | ||
| global.animation_frame = 0; | ||
| global.max_progress = 0.0; | ||
| global.fake_progress = 0.0; | ||
| global.fake_progress_start_time = 0.0; | ||
| } | ||
|
|
||
| fun stop_fake_progress() { | ||
| global.fake_progress_active = 0; | ||
| } | ||
|
|
||
| #----------------------------------------- Dialogue -------------------------------- | ||
|
|
||
|
|
@@ -119,7 +108,7 @@ entry.image = Image("entry.png"); | |
| bullet.image = Image("bullet.png"); | ||
|
|
||
| entry.sprite = Sprite(entry.image); | ||
| entry.x = Window.GetWidth()/2 - entry.image.GetWidth() / 2; | ||
| entry.x = Window.GetWidth() / 2 - entry.image.GetWidth() / 2; | ||
| entry.y = logo.sprite.GetY() + logo.image.GetHeight() + 40; | ||
| entry.sprite.SetPosition(entry.x, entry.y, 10001); | ||
| entry.sprite.SetOpacity(0); | ||
|
|
@@ -133,65 +122,60 @@ lock_width = 84 * lock_scale; | |
| scaled_lock = lock.image.Scale(lock_width, lock_height); | ||
| lock.sprite = Sprite(scaled_lock); | ||
| lock.x = entry.x - lock_width - 15; | ||
| lock.y = entry.y + entry.image.GetHeight()/2 - lock_height/2; | ||
| lock.y = entry.y + entry.image.GetHeight() / 2 - lock_height / 2; | ||
| lock.sprite.SetPosition(lock.x, lock.y, 10001); | ||
| lock.sprite.SetOpacity(0); | ||
|
|
||
| # Bullet array | ||
| bullet.sprites = []; | ||
|
|
||
| fun display_normal_callback () | ||
| { | ||
| hide_password_dialog(); | ||
|
|
||
| # Get current mode | ||
| mode = Plymouth.GetMode(); | ||
|
|
||
| # Only show progress bar for boot and resume modes | ||
| if ((mode == "boot" || mode == "resume") && global.password_shown == 1) | ||
| { | ||
| show_progress_bar(); | ||
| start_fake_progress(); | ||
| } | ||
| fun display_normal_callback() { | ||
| hide_password_dialog(); | ||
|
|
||
| # Get current mode | ||
| mode = Plymouth.GetMode(); | ||
|
|
||
| # Only show progress bar for boot and resume modes | ||
| if ((mode == "boot" || mode == "resume") && global.password_shown == 1) { | ||
| show_progress_bar(); | ||
| start_fake_progress(); | ||
| } | ||
| } | ||
|
|
||
| fun display_password_callback (prompt, bullets) | ||
| { | ||
| global.password_shown = 1; # Mark that password dialog has been shown | ||
|
|
||
| # Reset progress when password dialog appears | ||
| stop_fake_progress(); | ||
| hide_progress_bar(); | ||
| global.max_progress = 0.0; | ||
| global.fake_progress = 0.0; | ||
| global.real_progress = 0.0; | ||
| show_password_dialog(); | ||
|
|
||
| # Clear all bullets first | ||
| for (index = 0; bullet.sprites[index]; index++) | ||
| bullet.sprites[index].SetOpacity(0); | ||
|
|
||
| # Create and show bullets for current password (max 21) | ||
| max_bullets = 21; | ||
| bullets_to_show = bullets; | ||
| if (bullets_to_show > max_bullets) | ||
| bullets_to_show = max_bullets; | ||
|
|
||
| for (index = 0; index < bullets_to_show; index++) | ||
| { | ||
| if (!bullet.sprites[index]) | ||
| { | ||
| # Scale bullet image to 7x7 pixels | ||
| scaled_bullet = bullet.image.Scale(7, 7); | ||
| bullet.sprites[index] = Sprite(scaled_bullet); | ||
| bullet.x = entry.x + 20 + index * (7 + 5); | ||
| bullet.y = entry.y + entry.image.GetHeight() / 2 - 3.5; | ||
| bullet.sprites[index].SetPosition(bullet.x, bullet.y, 10002); | ||
| } | ||
| bullet.sprites[index].SetOpacity(1); | ||
| } | ||
| fun display_password_callback(prompt, bullets) { | ||
| global.password_shown = 1; # Mark that password dialog has been shown | ||
|
|
||
| # Stop fake progress when password dialog appears | ||
| stop_fake_progress(); | ||
| hide_progress_bar(); | ||
| show_password_dialog(); | ||
|
|
||
| # Clear all bullets first | ||
| for (index = 0; bullet.sprites[index]; index++) { | ||
| bullet.sprites[index].SetOpacity(0); | ||
| } | ||
|
|
||
| # Create and show bullets for current password (max 21) | ||
| max_bullets = 21; | ||
| bullets_to_show = bullets; | ||
| if (bullets_to_show > max_bullets) { | ||
| bullets_to_show = max_bullets; | ||
| } | ||
|
|
||
| for (index = 0; index < bullets_to_show; index++) { | ||
| if (!bullet.sprites[index]) { | ||
| # Scale bullet image to 7x7 pixels | ||
| scaled_bullet = bullet.image.Scale(7, 7); | ||
| bullet.sprites[index] = Sprite(scaled_bullet); | ||
| bullet.x = entry.x + 20 + index * (7 + 5); | ||
| bullet.y = entry.y + entry.image.GetHeight() / 2 - 3.5; | ||
| bullet.sprites[index].SetPosition(bullet.x, bullet.y, 10002); | ||
| } | ||
|
|
||
| bullet.sprites[index].SetOpacity(1); | ||
| } | ||
| } | ||
|
|
||
| Plymouth.SetDisplayNormalFunction(display_normal_callback); | ||
| Plymouth.SetDisplayPasswordFunction(display_password_callback); | ||
|
|
||
|
|
@@ -214,44 +198,38 @@ progress_bar.y = progress_box.y + (progress_box.image.GetHeight() - progress_bar | |
| progress_bar.sprite.SetPosition(progress_bar.x, progress_bar.y, 1); | ||
| progress_bar.sprite.SetOpacity(0); | ||
|
|
||
| fun progress_callback (duration, progress) | ||
| { | ||
| global.real_progress = progress; | ||
|
|
||
| # If real progress is above limit, stop fake progress and use real progress | ||
| if (progress > global.fake_progress_limit) | ||
| { | ||
| stop_fake_progress(); | ||
| update_progress_bar(progress); | ||
| } | ||
| fun progress_callback(duration, progress) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the main change. The However, while the drive is being decrypted, the To handle this, we record the We switch to showing real progress once the fake progress values are exceeded so that there's a seamless handoff. |
||
| # Track when fake progress starts | ||
| # Needed because duration and progress freeze during drive decryption | ||
| if (global.fake_progress_start_time == 0.0) { | ||
| global.fake_progress_start_time = duration; | ||
| } | ||
|
|
||
| Plymouth.SetBootProgressFunction(progress_callback); | ||
|
|
||
| #----------------------------------------- Quit -------------------------------- | ||
| global.real_progress = progress; | ||
|
|
||
| fun quit_callback () | ||
| { | ||
| logo.sprite.SetOpacity (1); | ||
| # Use real progress once its unfrozen and exceeds fake progress | ||
| if (duration > global.fake_progress_start_time && progress > global.fake_progress) { | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I experimented with not tracking the starting duration at all and only having However, there are certain scenarios where the "frozen" real progress value is greater than the fake progress value as soon as the progress bar is shown, which results in only the real progress being shown the entire boot. |
||
| stop_fake_progress(); | ||
| update_progress_bar(progress); | ||
| } | ||
| } | ||
|
|
||
| Plymouth.SetQuitFunction(quit_callback); | ||
| Plymouth.SetBootProgressFunction(progress_callback); | ||
|
|
||
| #----------------------------------------- Message -------------------------------- | ||
|
|
||
| message_sprite = Sprite(); | ||
| message_sprite.SetPosition(10, 10, 10000); | ||
|
|
||
| fun display_message_callback (text) | ||
| { | ||
| my_image = Image.Text(text, 1, 1, 1); | ||
| message_sprite.SetImage(my_image); | ||
| fun display_message_callback(text) { | ||
| message = Image.Text(text, 1, 1, 1); | ||
| message_sprite.SetImage(message); | ||
| message_sprite.SetOpacity(1); | ||
| } | ||
|
|
||
| fun hide_message_callback (text) | ||
| { | ||
| fun hide_message_callback(text) { | ||
| message_sprite.SetOpacity(0); | ||
| } | ||
|
|
||
| Plymouth.SetDisplayMessageFunction (display_message_callback); | ||
| Plymouth.SetHideMessageFunction (hide_message_callback); | ||
| Plymouth.SetDisplayMessageFunction(display_message_callback); | ||
| Plymouth.SetHideMessageFunction(hide_message_callback); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| echo "Update Plymouth theme for a smoother progress bar animation" | ||
| omarchy-refresh-plymouth |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Due to all the formatting changes, it's easier to review this file with whitespace hidden.