From ec1e9a9b59ee8dcd49c456091681ef5db59b2070 Mon Sep 17 00:00:00 2001 From: kenji Date: Mon, 5 Jan 2026 13:20:54 -0600 Subject: [PATCH] feat(plymouth): implement custom hakase theme based on omarchy assets --- apps/plymouth/theme.nix | 17 ++ apps/plymouth/themes/hakase/bullet.png | Bin 0 -> 293 bytes apps/plymouth/themes/hakase/entry.png | Bin 0 -> 694 bytes apps/plymouth/themes/hakase/hakase.plymouth | 11 + apps/plymouth/themes/hakase/hakase.script | 257 +++++++++++++++++++ apps/plymouth/themes/hakase/lock.png | Bin 0 -> 1537 bytes apps/plymouth/themes/hakase/logo.png | Bin 0 -> 3072 bytes apps/plymouth/themes/hakase/progress_bar.png | Bin 0 -> 314 bytes apps/plymouth/themes/hakase/progress_box.png | Bin 0 -> 314 bytes modules/nixos/boot.nix | 21 +- 10 files changed, 300 insertions(+), 6 deletions(-) create mode 100644 apps/plymouth/theme.nix create mode 100644 apps/plymouth/themes/hakase/bullet.png create mode 100644 apps/plymouth/themes/hakase/entry.png create mode 100644 apps/plymouth/themes/hakase/hakase.plymouth create mode 100644 apps/plymouth/themes/hakase/hakase.script create mode 100644 apps/plymouth/themes/hakase/lock.png create mode 100644 apps/plymouth/themes/hakase/logo.png create mode 100644 apps/plymouth/themes/hakase/progress_bar.png create mode 100644 apps/plymouth/themes/hakase/progress_box.png diff --git a/apps/plymouth/theme.nix b/apps/plymouth/theme.nix new file mode 100644 index 0000000..a2b6c7e --- /dev/null +++ b/apps/plymouth/theme.nix @@ -0,0 +1,17 @@ +{ pkgs }: + +pkgs.stdenv.mkDerivation { + pname = "hakase-plymouth-theme"; + version = "1.0"; + + src = ./themes/hakase; + + installPhase = '' + mkdir -p $out/share/plymouth/themes/hakase + cp * $out/share/plymouth/themes/hakase/ + + # Patch the .plymouth file to point to the store path + sed -i "s@ImageDir=.*@ImageDir=$out/share/plymouth/themes/hakase@" $out/share/plymouth/themes/hakase/hakase.plymouth + sed -i "s@ScriptFile=.*@ScriptFile=$out/share/plymouth/themes/hakase/hakase.script@" $out/share/plymouth/themes/hakase/hakase.plymouth + ''; +} diff --git a/apps/plymouth/themes/hakase/bullet.png b/apps/plymouth/themes/hakase/bullet.png new file mode 100644 index 0000000000000000000000000000000000000000..62249b3bd82b0b670a8a503a3bb69bbfe79cc4f4 GIT binary patch literal 293 zcmeAS@N?(olHy`uVBq!ia0vp^d?3uh1|;P@bT0xamSQK*5Dp-y;YjHK^6z-MIEH9U zoIA;|uZcmx^}qZQCjEr9ou<6JoZt7m0FRe~3I-q?kdfVZu-@kQlF=GdcP{_{ literal 0 HcmV?d00001 diff --git a/apps/plymouth/themes/hakase/entry.png b/apps/plymouth/themes/hakase/entry.png new file mode 100644 index 0000000000000000000000000000000000000000..5c7891792b8872db933f17a667a449a478c0be53 GIT binary patch literal 694 zcmeAS@N?(olHy`uVBq!ia0y~yV3Y&04FuSLa@a2CEqi4B`cIb_Lo1CD@X@-Ch2J0cXVbJ3tZk5>H=O_LuA|BAOCC zHaNEIWB8gpRxhIrP-q3h6ugFYL#HXzL zs@XQt|A*ck*2`WtwNt_B=Yyw}k1W;qd^{d$X}`l_w|(sWyd1OlR=bb4vobhzyu2J; zZnSHU?(>Dgw^zkpzx{Xds&+;O9q+{%->h5%r*w;NJ|6l0@y}PwUNtf>?BDuhVsPE} zg8wPcOLO0+EPi#6fx-T(fyDNE?akAVe!dFQ`p5hrGs6*K8wLlPp~KkoX?J(~!5v@i zWcGyE3NSoazmLT~cXe58{`dY>a%>D6be?{Bxz<`T_MTZ++WO$HQVb2Mv&zq@Pirx0 z4OBAy4vaL_64!{5l*E!$tK_0oAjM#0U}UOmV6JOm5@KXvWnyS$WTI_gU}a!%k#!|7 zD3LVe=BH$)RpQpLAVSa+oZUJsR-+c`p0rfC=y85}Sb4q9e01cG# AHUIzs literal 0 HcmV?d00001 diff --git a/apps/plymouth/themes/hakase/hakase.plymouth b/apps/plymouth/themes/hakase/hakase.plymouth new file mode 100644 index 0000000..9935630 --- /dev/null +++ b/apps/plymouth/themes/hakase/hakase.plymouth @@ -0,0 +1,11 @@ +[Plymouth Theme] +Name=Omarchy +Description=Omarchy splash screen. +ModuleName=script + +[script] +ImageDir=/usr/share/plymouth/themes/omarchy +ScriptFile=/usr/share/plymouth/themes/omarchy/omarchy.script +ConsoleLogBackgroundColor=0x1a1b26 +MonospaceFont=Cantarell 11 +Font=Cantarell 11 diff --git a/apps/plymouth/themes/hakase/hakase.script b/apps/plymouth/themes/hakase/hakase.script new file mode 100644 index 0000000..df0c298 --- /dev/null +++ b/apps/plymouth/themes/hakase/hakase.script @@ -0,0 +1,257 @@ +# Omarchy Plymouth Theme Script + +Window.SetBackgroundTopColor(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); + +# 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.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.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); + } + } + + +Plymouth.SetRefreshFunction (refresh_callback); + +#----------------------------------------- Helper Functions -------------------------------- + +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); + } + } + +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 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() + { + # 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; + } + +fun stop_fake_progress() + { + global.fake_progress_active = 0; + } + +#----------------------------------------- Dialogue -------------------------------- + +lock.image = Image("lock.png"); +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.y = logo.sprite.GetY() + logo.image.GetHeight() + 40; +entry.sprite.SetPosition(entry.x, entry.y, 10001); +entry.sprite.SetOpacity(0); + +# Scale lock to be slightly shorter than entry field height +# Original lock is 84x96, entry height determines scale +lock_height = entry.image.GetHeight() * 0.8; +lock_scale = lock_height / 96; +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.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_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); + } + } + +Plymouth.SetDisplayNormalFunction(display_normal_callback); +Plymouth.SetDisplayPasswordFunction(display_password_callback); + +#----------------------------------------- Progress Bar -------------------------------- + +progress_box.image = Image("progress_box.png"); +progress_box.sprite = Sprite(progress_box.image); + +progress_box.x = Window.GetWidth() / 2 - progress_box.image.GetWidth() / 2; +progress_box.y = entry.y + entry.image.GetHeight() / 2 - progress_box.image.GetHeight() / 2; +progress_box.sprite.SetPosition(progress_box.x, progress_box.y, 0); +progress_box.sprite.SetOpacity(0); + +progress_bar.original_image = Image("progress_bar.png"); +progress_bar.sprite = Sprite(); +progress_bar.image = progress_bar.original_image.Scale(1, progress_bar.original_image.GetHeight()); + +progress_bar.x = Window.GetWidth() / 2 - progress_bar.original_image.GetWidth() / 2; +progress_bar.y = progress_box.y + (progress_box.image.GetHeight() - progress_bar.original_image.GetHeight()) / 2; +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); + } + } + +Plymouth.SetBootProgressFunction(progress_callback); + +#----------------------------------------- Quit -------------------------------- + +fun quit_callback () +{ + logo.sprite.SetOpacity (1); +} + +Plymouth.SetQuitFunction(quit_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 hide_message_callback (text) +{ + message_sprite.SetOpacity(0); +} + +Plymouth.SetDisplayMessageFunction (display_message_callback); +Plymouth.SetHideMessageFunction (hide_message_callback); diff --git a/apps/plymouth/themes/hakase/lock.png b/apps/plymouth/themes/hakase/lock.png new file mode 100644 index 0000000000000000000000000000000000000000..3046de1b6a3f20d194f010fee671c936326beb27 GIT binary patch literal 1537 zcmaKseKgYx7{|AHUkq{cvLRb`y@V2`mnmXdjLEhr)$(pBuF;Fg7MiYcO37PGhEj}Q zjkx2~FG}6iyi`n^Yf__8QcEw2yZ-5)d(J)Q^E}@_p65B=bDrm%=QKHZn*rPs4uL=n zhyi{()JjnYT~}M3b6C_2wdkb;L@*(cWhOrZ3aPkg4uNP1iGDtmqZi-uNcB{I6qr4G z#i8)RDVJ5cL1!Nt{l+uGVeBIuqiPA1leLuzROguk=T?Ra9MON;!yO9zRCRCo6Z~dO zZEkEpocTDP92U}<@#4T*rj2a+(x$Pv@seyCNY93qf#zr(*_!G?;{%hf$7{)0wjI)D_X( zm}u}Ln8`jY-p@J5VO8CjI08(3E0;){?Q-|cD59AsumI2J9^x48{P#=1M@1> z`3H_14&4M@jmRG{e0-T%a$_};xp+UGO5W(w?k>lx9F;;;TAULlesb+OonM22Q!c81az}6c*t>v7~tOVPm zhU!UpisjNWSukBNpiE6b9Q+!olgTZa?zW>>3@H1~d|7^cD&u?XCD2;IcY{V&A|v%} zdgu$wb94J!*DI4eckMCCA2lAHSD>gLRdsonw|WzWbCTnPs>+EhLc?+2mx$6;Pv&90 z`0L*_g4}j}dR=HC;<$8$V8H_R+5)ZzLV8t}3+GKlEaK#JdXNo5RObC<{#qWnTDYlq z)?=4}W)I5KWoU07<8~IqmNFI8_hC%4r#B}#Xw19S$T7|LGujzt$Su_wD^oXACsp6Y z@KIlS8YxqxORtv@zY6`OX~XEy`ZfP$tM|_>py#PwsWV|^yI&|}5<#J-3naFWlPt&0 zMM1pA=0DF$cD`C*@wVbov-TLG5Xo`|Cp3#t1lw$AGfc{*?c{bf87cSbWy55&%tM%p zeXIrPK75<{GLPwF=XCHZ+fTLU^X`|rK`Cd;+jrMtGa(^cXr+} zgVU;=CUsvl!V_DzOK)XsWtP;Abb!{3qlSAP;8kvbDCe<{Y>C!!vz1@u+lCa}en)#) z;t*rZHexumT)tUH-d4SN1?85h(q5JM8U5Fz3%*u)MG1WJ{{KN~pSIm6Hn6(LH){pv ze0V$g{08_ag*>vOzNWv9Sb-lDoN*GiYdmB~>U7k^= z!sR4-SaIB}#^(07LNV=C+^Wxvp#$8peZDi2`#_iZs= zqu`0Tb?EN1+Q1#UGG4l`!VHb{C~R_k3u*CyVN%VraV9KWDT5xw*ws95W?`)tyZ4)2<_Ue8M2_Lb zaSg1=`gadV(~oTBd5ZNyUQ=(QUE*OKK)-|hxBH_7i+@D@94nGF6k#qnb#ZuUGt_nK Vg$$|X5%o`p5dDMwntbUe{{_St%Ul2e literal 0 HcmV?d00001 diff --git a/apps/plymouth/themes/hakase/logo.png b/apps/plymouth/themes/hakase/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..f185b2660763bebf2343775d2a682133d5bf78d2 GIT binary patch literal 3072 zcmb7`dpwi<8^>>l=qXa_p(N|vOGGPYhGLi+QCbc)wX$TC#G18HejOx@XQ8JxE6K`X zni(~wJcT0b$sxxnDQvUJlO34D@2)?7uU^03^Splfp+o^$^kjx+aiZi_`>w)u{kD<}Yyv zeX!A-4*(fnfCd1x%wG)v?j{=A0N}GO8MHgnS8w=p-P`fatOEAO!K#L|&c*B8BJ5M< zKTSV+EHKwLpyqmJcfOpk{n|E_MGu=qNKy`O^J;{ui5ORG$7dcaKi;t+n=PAUG1*BC z9+J4%*>d_gEW)Fd84A6l&>FGCJa;HhxZB%z1KHOuXXIuXGIZREkfeQiu2;uQ#xJKp zEx4|f5M>JEU>yWf;lByG)84pCVe-XJWr3+F2(wM9M&-E}kQE*iGsPs{%609P)>asy zT&O#<_&uZC-vFaEE=_9+!IPBdnX~w_c=C|SQs<+QB^ZPBpNv7G!CDu-L+@yzIg+z9 zD%|iG^G#dhgzS`7@Fj(8`VCL)TLOWX)QX4WtojaImapJK?-JH0mQ)oqKsVG?iR$Sc zpAc=7r~#9$I*f}#~e0iq6IL%hfZ)qb6 zaAN%3S z+lpX@bZ4-hBXqiQR?Tqk2o0sT=InY1fv<|WXoVqpr3c^OK`dmnv=kz8yNE60>F$}< zl(ILc$m{xig^`rdp-EFtjxnVG)trDwnxUecHk6<<-FdHGsxBkXg&00rDU-2PYpbPDT%+$8$2anSv(+QmRX&$t= zz#RQ%RIf5XIk2?qA6XLrw>{Un4C%^pW1hXtMxJn=bsZRJVdQ|ysSx_ zCiyVv{ZdgS$-UXvMkAj#)Cvwnhb=_zWn`J9b!|EDU+Jg4cn&)K$;ELN0Pybpv2+!W zQjDrZk6H$a4VyHI^(w(X#kiRMo7IAh3d$s3m_Is}0?g{g4k^TVhNw<=JJaX50jfO|E6`y!)ZlW>SU`3ue<5~ z3ufkPwENsnBwU|<$W)N{s~ytPV^mqGQ+Bzwgjyqcfq%VCudAT~qyB3~_VyTVzJ_+V zepU#>yF?B3+*?ZO%bJHy-@Hm^y>J&H^nBw&NKV5I}W0OZ=765$6i{evd{UOT!7aRo=*Ni|i7Xe*2lwS?2{sg!tqMWjDA#uYXtkjQ;OHjz4q7}b@A zV}+@&B!WK>x96?5*x(vj+5>^}oYMP;(<~x_N$fUNwK-M5MdhuCu@0380u}WEvoIZ- zho9fZn|}kg?R#fe(Pk&U!)@-Gqt5_CjzTNR$b$6-Z?B4ynOS!;sVrXW*OcHT?si?D zx4qjmTAHe&W9pB$cSb@Ij&McacNy!Lo%|HU;296;dgxpdIgO;3RyIoT#P+1dIh|&& zcyIYCR1S(Qgh62}e0Rg_m0vsXR|jrJAM8MZ1uTul3luX0B;-fiPV9DiQ-`p9g||HM zVz%7-Q+`p5WO5P-jKa}jTO=*U>f$OeqKcuJHJ1t2aN_G49HlF-D8{*hNXPOuUrr0L z_Oo3Glu%UHPgcWA&jBLmy4crb`N^kG93!3mds{~rDR%v`{|cg}=)NaG{S2C7;QOCH zC!BPNQr25Q-1C0F-8^c_sB`bV0JfMv(c68O0J3z)zev#y?X;o@bxiT&iM|sCYo<0- z+?pKW;pk`Dqu+P?F}%82%%=Or%ux1~4G)k?&-S;p-F{+9tkJ9NSb;)@-xcDYud0Tf zbS4gDNa7+%nM$Vh3?J-CB&P+7_y#swE5lRx4sK_AT$|T+e5~!G_GFHXPkng6ABJ5& zU*10;%b=duu)8=Xg|9ec=mZpB3^~+M^4BmU3+G>9t(A=44QM1k4f?43Dja|VTOH<9 bg1%yVhS4QqD-cs);|n0*ex9tI;TQf5WcB8Z literal 0 HcmV?d00001 diff --git a/apps/plymouth/themes/hakase/progress_bar.png b/apps/plymouth/themes/hakase/progress_bar.png new file mode 100644 index 0000000000000000000000000000000000000000..dbb9fd74fc853d8a79ff072f5070cc660ea11897 GIT binary patch literal 314 zcmeAS@N?(olHy`uVBq!ia0y~yVAKJ!xfq#&q>16jl|V`%*(1o8fuTx`fuW&=f#DZW zsNn?zL#Y7+!>a@a2CEqi4B`cIb_Lo1CD;OdLR?pFy!QY9|E)n1Qa};LByV>Y#{W#Z z_kbMs5>H=O_LuA|;<}1kzlXd83h{cnIEF}EPEL?uW#(sKY-3>5a*dk16jl|V`%*(1o8fuTx`fuW&=f#DZW zsNn?zL#Y7+!>a@a2CEqi4B`cIb_Lo1CD;OdLR>ZVoc{m+e`?yP4?q#dByV>Y#{W#Z z_kbMs5>H=O_LuA|;=1xB(>$GkLcE?Xjv*44lM^IZnfVzQ+ZY(NT;t{dB~(jXBT7;d zOH!?pi&B9UgOP!esjh*!u7OF2k+GGDsg<#zwt<0_fx+)>UOXroa`RI%(<*Um=z9HW l15kqo+=i0O+|=Td#M}bhdhY(aoDbB);OXk;vd$@?2>|VqQP%(f literal 0 HcmV?d00001 diff --git a/modules/nixos/boot.nix b/modules/nixos/boot.nix index 38aedc7..26008ca 100644 --- a/modules/nixos/boot.nix +++ b/modules/nixos/boot.nix @@ -17,14 +17,23 @@ initrd = { systemd.enable = true; }; + + kernelParams = [ + "quiet" + "splash" + "boot.shell_on_fail" + "loglevel=3" + "rd.systemd.show_status=false" + "rd.udev.log_level=3" + "udev.log_priority=3" + ]; + plymouth = { enable = true; - # themePackages = [ - # (pkgs.adi1090x-plymouth-themes.override { - # selected_themes = ["circle_hud"]; - # }) - # ]; - # theme = "circle_hud"; + themePackages = [ + (pkgs.callPackage ../../apps/plymouth/theme.nix {}) + ]; + theme = "hakase"; }; }; }