1# Arch Linux Plymouth Theme Script
2
3Window.SetBackgroundTopColor(0, 0, 0);
4Window.SetBackgroundBottomColor(0, 0, 0);
5
6logo.original_image = Image("logo.png");
7
8# Scale logo to at most 40% of screen height
9logo.max_height = Math.Int(Window.GetHeight() * 0.40);
10if (logo.original_image.GetHeight() > logo.max_height)
11 {
12 logo.scale = logo.max_height / logo.original_image.GetHeight();
13 logo.image = logo.original_image.Scale(
14 Math.Int(logo.original_image.GetWidth() * logo.scale),
15 logo.max_height
16 );
17 }
18else
19 {
20 logo.image = logo.original_image;
21 }
22
23logo.sprite = Sprite(logo.image);
24
25# Center the logo during boot
26logo.normal_x = Window.GetWidth() / 2 - logo.image.GetWidth() / 2;
27logo.normal_y = Window.GetHeight() / 2 - logo.image.GetHeight() / 2;
28logo.sprite.SetPosition(logo.normal_x, logo.normal_y, 1);
29logo.sprite.SetOpacity(1);
30
31# Use these to adjust the progress bar timing
32global.fake_progress_limit = 0.7; # Target percentage for fake progress (0.0 to 1.0)
33global.fake_progress_duration = 15.0; # Duration in seconds to reach limit
34
35# Progress bar animation variables
36global.fake_progress = 0.0;
37global.real_progress = 0.0;
38global.fake_progress_active = 0; # 0 / 1 boolean
39global.animation_frame = 0;
40global.fake_progress_start_time = 0; # Track when fake progress started
41global.password_shown = 0; # Track if password dialog has been shown
42global.max_progress = 0.0; # Track the maximum progress reached to prevent backwards movement
43
44fun refresh_callback ()
45 {
46 global.animation_frame++;
47
48 # Animate fake progress to limit over time with easing
49 if (global.fake_progress_active == 1)
50 {
51 # Calculate elapsed time since start
52 elapsed_time = global.animation_frame / 50.0; # Convert frames to seconds (50 FPS)
53
54 # Calculate linear progress ratio (0 to 1) based on time
55 time_ratio = elapsed_time / global.fake_progress_duration;
56 if (time_ratio > 1.0)
57 time_ratio = 1.0;
58
59 # Apply easing curve: ease-out quadratic
60 # Formula: 1 - (1 - x)^2
61 eased_ratio = 1 - ((1 - time_ratio) * (1 - time_ratio));
62
63 # Calculate fake progress based on eased ratio
64 global.fake_progress = eased_ratio * global.fake_progress_limit;
65
66 # Update progress bar with fake progress
67 update_progress_bar(global.fake_progress);
68 }
69 }
70
71
72Plymouth.SetRefreshFunction (refresh_callback);
73
74#----------------------------------------- Helper Functions --------------------------------
75
76fun update_progress_bar(progress)
77 {
78 # Only update if progress is moving forward
79 if (progress > global.max_progress)
80 {
81 global.max_progress = progress;
82 width = Math.Int(progress_bar.original_image.GetWidth() * progress);
83 if (width < 1) width = 1; # Ensure minimum width of 1 pixel
84
85 progress_bar.image = progress_bar.original_image.Scale(width, progress_bar.original_image.GetHeight());
86 progress_bar.sprite.SetImage(progress_bar.image);
87 }
88 }
89
90fun show_progress_bar()
91 {
92 progress_box.sprite.SetOpacity(1);
93 progress_bar.sprite.SetOpacity(1);
94 }
95
96fun hide_progress_bar()
97 {
98 progress_box.sprite.SetOpacity(0);
99 progress_bar.sprite.SetOpacity(0);
100 }
101
102fun show_password_dialog()
103 {
104 entry.sprite.SetOpacity(1);
105 }
106
107fun hide_password_dialog()
108 {
109 entry.sprite.SetOpacity(0);
110 for (index = 0; bullet.sprites[index]; index++)
111 bullet.sprites[index].SetOpacity(0);
112 }
113
114fun start_fake_progress()
115 {
116 # Don't reset if we already have progress
117 if (global.max_progress == 0.0)
118 {
119 global.fake_progress = 0.0;
120 global.real_progress = 0.0;
121 update_progress_bar(0.0);
122 }
123 global.fake_progress_active = 1;
124 global.animation_frame = 0;
125 }
126
127fun stop_fake_progress()
128 {
129 global.fake_progress_active = 0;
130 }
131
132#----------------------------------------- Dialogue --------------------------------
133
134entry.image = Image("entry.png");
135bullet.image = Image("bullet.png");
136
137# Scale entry to 60% of its original size
138entry.scaled_image = entry.image.Scale(
139 Math.Int(entry.image.GetWidth() * 0.6),
140 Math.Int(entry.image.GetHeight() * 0.6)
141);
142
143# Entry appears below the centered logo with a fixed gap
144dialog_gap = 32;
145entry.dialog_y = logo.normal_y + logo.image.GetHeight() + dialog_gap;
146
147# Safety cap: never push entry off screen
148entry.max_y = Window.GetHeight() - entry.scaled_image.GetHeight() - 20;
149if (entry.dialog_y > entry.max_y)
150 entry.dialog_y = entry.max_y;
151
152entry.x = Window.GetWidth() / 2 - entry.scaled_image.GetWidth() / 2;
153
154entry.sprite = Sprite(entry.scaled_image);
155entry.sprite.SetPosition(entry.x, entry.dialog_y, 10001);
156entry.sprite.SetOpacity(0);
157
158# Bullet array
159bullet.sprites = [];
160
161fun display_normal_callback ()
162 {
163 hide_password_dialog();
164
165 # Restore logo to center when back to normal boot screen
166 logo.sprite.SetPosition(logo.normal_x, logo.normal_y, 1);
167
168 # Get current mode
169 mode = Plymouth.GetMode();
170
171 # Only show progress bar for boot and resume modes
172 if ((mode == "boot" || mode == "resume") && global.password_shown == 1)
173 {
174 show_progress_bar();
175 start_fake_progress();
176 }
177 }
178
179fun display_password_callback (prompt, bullets)
180 {
181 global.password_shown = 1; # Mark that password dialog has been shown
182
183 # Keep logo at its normal centered position during unlock
184 logo.sprite.SetPosition(logo.normal_x, logo.normal_y, 1);
185
186 # Reset progress when password dialog appears
187 stop_fake_progress();
188 hide_progress_bar();
189 global.max_progress = 0.0;
190 global.fake_progress = 0.0;
191 global.real_progress = 0.0;
192 show_password_dialog();
193
194 # Clear all bullets first
195 for (index = 0; bullet.sprites[index]; index++)
196 bullet.sprites[index].SetOpacity(0);
197
198 # Create and show bullets for current password (max 21)
199 max_bullets = 21;
200 bullets_to_show = bullets;
201 if (bullets_to_show > max_bullets)
202 bullets_to_show = max_bullets;
203
204 for (index = 0; index < bullets_to_show; index++)
205 {
206 if (!bullet.sprites[index])
207 {
208 # Scale bullet image to 6x6 pixels
209 scaled_bullet = bullet.image.Scale(6, 6);
210 bullet.sprites[index] = Sprite(scaled_bullet);
211 bullet.x = entry.x + 12 + index * (6 + 4);
212 bullet.y = entry.dialog_y + entry.scaled_image.GetHeight() / 2 - 3;
213 bullet.sprites[index].SetPosition(bullet.x, bullet.y, 10002);
214 }
215 bullet.sprites[index].SetOpacity(1);
216 }
217 }
218
219Plymouth.SetDisplayNormalFunction(display_normal_callback);
220Plymouth.SetDisplayPasswordFunction(display_password_callback);
221
222#----------------------------------------- Progress Bar --------------------------------
223
224progress_box.image = Image("progress_box.png");
225progress_box.sprite = Sprite(progress_box.image);
226
227progress_box.x = Window.GetWidth() / 2 - progress_box.image.GetWidth() / 2;
228progress_box.y = logo.normal_y + logo.image.GetHeight() + 40;
229progress_box.sprite.SetPosition(progress_box.x, progress_box.y, 0);
230progress_box.sprite.SetOpacity(0);
231
232progress_bar.original_image = Image("progress_bar.png");
233progress_bar.sprite = Sprite();
234progress_bar.image = progress_bar.original_image.Scale(1, progress_bar.original_image.GetHeight());
235
236progress_bar.x = Window.GetWidth() / 2 - progress_bar.original_image.GetWidth() / 2;
237progress_bar.y = progress_box.y + (progress_box.image.GetHeight() - progress_bar.original_image.GetHeight()) / 2;
238progress_bar.sprite.SetPosition(progress_bar.x, progress_bar.y, 1);
239progress_bar.sprite.SetOpacity(0);
240
241fun progress_callback (duration, progress)
242 {
243 global.real_progress = progress;
244
245 # If real progress is above limit, stop fake progress and use real progress
246 if (progress > global.fake_progress_limit)
247 {
248 stop_fake_progress();
249 update_progress_bar(progress);
250 }
251 }
252
253Plymouth.SetBootProgressFunction(progress_callback);
254
255#----------------------------------------- Quit --------------------------------
256
257fun quit_callback ()
258{
259 logo.sprite.SetOpacity (1);
260}
261
262Plymouth.SetQuitFunction(quit_callback);
263
264#----------------------------------------- Message --------------------------------
265
266message_sprite = Sprite();
267message_sprite.SetPosition(10, 10, 10000);
268
269fun display_message_callback (text)
270{
271 my_image = Image.Text(text, 1, 1, 1);
272 message_sprite.SetImage(my_image);
273}
274
275fun hide_message_callback (text)
276{
277 message_sprite.SetOpacity(0);
278}
279
280Plymouth.SetDisplayMessageFunction (display_message_callback);
281Plymouth.SetHideMessageFunction (hide_message_callback);