1
0

Splash.swift 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. //
  2. // ARK-OS Splash Animation (Wayland/4K Ready)
  3. //
  4. // Renders a high-definition, dynamic 4K animation of the ARK atom.
  5. //
  6. import Foundation
  7. // Core parameters (dynamically scaled in drawSwiftSplash)
  8. var NUC_R: Double = 28.0
  9. var INNER_R: Double = 120.0
  10. var OUTER_R: Double = 200.0
  11. var ELEC_R: Double = 12.0
  12. var RING_T: Double = 2.0
  13. public var scrW: Int = 1920
  14. public var scrH: Int = 1080
  15. public var bpp: Int = 4
  16. public var lineLen: Int = 1920 * 4
  17. // The Wayland client handle for the splash screen
  18. var splashGraphics: ArkGraphics!
  19. var renderBuffer: UnsafeMutablePointer<UInt8>!
  20. public func emitFrame() {
  21. splashGraphics.pan()
  22. usleep(16000) // basic ~60fps pacing
  23. }
  24. func presentScreen() {
  25. emitFrame()
  26. }
  27. func clearScreen(r: UInt8, g: UInt8, b: UInt8) {
  28. if renderBuffer == nil { return }
  29. let count = scrW * scrH * bpp
  30. for i in stride(from: 0, to: count, by: bpp) {
  31. renderBuffer[i] = b
  32. renderBuffer[i+1] = g
  33. renderBuffer[i+2] = r
  34. if bpp == 4 { renderBuffer[i+3] = 255 }
  35. }
  36. }
  37. func drawRoundedRect(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8, a: UInt8, radius: Int) {
  38. if renderBuffer == nil { return }
  39. for i in max(0, y)..<min(scrH, y+h) {
  40. let rowBase = i * lineLen
  41. for j in max(0, x)..<min(scrW, x+w) {
  42. let off = rowBase + j * bpp
  43. renderBuffer[off] = b
  44. renderBuffer[off+1] = g
  45. renderBuffer[off+2] = r
  46. if bpp == 4 { renderBuffer[off+3] = a }
  47. }
  48. }
  49. }
  50. // Draw filled disk with anti-aliasing (proper premultiplied alpha)
  51. func diskAA(_ p: UnsafeMutablePointer<UInt8>, _ cx: Double, _ cy: Double, _ r: Double, _ r_col: UInt8, _ g_col: UInt8, _ b_col: UInt8, _ alpha: UInt8) {
  52. let y0 = max(0, Int(cy - r - 2))
  53. let y1 = min(scrH - 1, Int(cy + r + 2))
  54. let x0 = max(0, Int(cx - r - 2))
  55. let x1 = min(scrW - 1, Int(cx + r + 2))
  56. let rInnerSq = (r - 0.5) * (r - 0.5)
  57. let rOuter = r + 0.5
  58. let rOuterSq = rOuter * rOuter
  59. let fVal = Double(alpha) / 255.0
  60. let preR = Double(r_col) * fVal
  61. let preG = Double(g_col) * fVal
  62. let preB = Double(b_col) * fVal
  63. for y in y0...y1 {
  64. let dy = Double(y) - cy
  65. let dySq = dy * dy
  66. let rowBase = y * lineLen
  67. for x in x0...x1 {
  68. let dx = Double(x) - cx
  69. let distSq = dx * dx + dySq
  70. if distSq < rInnerSq {
  71. let off = rowBase + x * bpp
  72. let invA = 1.0 - fVal
  73. p[off] = UInt8(min(255, preB + Double(p[off]) * invA))
  74. p[off+1] = UInt8(min(255, preG + Double(p[off+1]) * invA))
  75. p[off+2] = UInt8(min(255, preR + Double(p[off+2]) * invA))
  76. if bpp == 4 {
  77. p[off+3] = UInt8(min(255, fVal * 255.0 + Double(p[off+3]) * invA))
  78. }
  79. } else if distSq < rOuterSq {
  80. let dist = sqrt(distSq)
  81. let edgeAlpha = fVal * (rOuter - dist)
  82. let invA = 1.0 - edgeAlpha
  83. let eB = Double(b_col) * edgeAlpha
  84. let eG = Double(g_col) * edgeAlpha
  85. let eR = Double(r_col) * edgeAlpha
  86. let off = rowBase + x * bpp
  87. p[off] = UInt8(min(255, eB + Double(p[off]) * invA))
  88. p[off+1] = UInt8(min(255, eG + Double(p[off+1]) * invA))
  89. p[off+2] = UInt8(min(255, eR + Double(p[off+2]) * invA))
  90. if bpp == 4 {
  91. p[off+3] = UInt8(min(255, edgeAlpha * 255.0 + Double(p[off+3]) * invA))
  92. }
  93. }
  94. }
  95. }
  96. }
  97. // Draw ring outline with anti-aliasing
  98. func ringAA(_ p: UnsafeMutablePointer<UInt8>, _ cx: Double, _ cy: Double, _ r: Double, _ t: Double, _ val: UInt8) {
  99. let scan = r + t + 2
  100. let y0 = max(0, Int(cy - scan))
  101. let y1 = min(scrH - 1, Int(cy + scan))
  102. let x0 = max(0, Int(cx - scan))
  103. let x1 = min(scrW - 1, Int(cx + scan))
  104. let rInMinSq = (r - t - 0.5) * (r - t - 0.5)
  105. let rInMaxSq = (r - t + 0.5) * (r - t + 0.5)
  106. let rOutMinSq = (r + t - 0.5) * (r + t - 0.5)
  107. let rOutMaxSq = (r + t + 0.5) * (r + t + 0.5)
  108. let tOuter = t + 0.5
  109. let fVal = Double(val) / 255.0
  110. let c = 255.0 * fVal // We draw white rings
  111. for y in y0...y1 {
  112. let dy = Double(y) - cy
  113. let dySq = dy * dy
  114. let rowBase = y * lineLen
  115. for x in x0...x1 {
  116. let dx = Double(x) - cx
  117. let d = dx * dx + dySq
  118. if d >= rInMaxSq && d <= rOutMinSq {
  119. let off = rowBase + x * bpp
  120. let invA = 1.0 - fVal
  121. p[off] = UInt8(min(255, c + Double(p[off]) * invA))
  122. p[off+1] = UInt8(min(255, c + Double(p[off+1]) * invA))
  123. p[off+2] = UInt8(min(255, c + Double(p[off+2]) * invA))
  124. if bpp == 4 {
  125. p[off+3] = UInt8(min(255, fVal * 255.0 + Double(p[off+3]) * invA))
  126. }
  127. } else if (d >= rInMinSq && d < rInMaxSq) || (d > rOutMinSq && d <= rOutMaxSq) {
  128. let dist = sqrt(d)
  129. let diff = abs(dist - r)
  130. let edgeA = fVal * (tOuter - diff)
  131. if edgeA > 0 {
  132. let invA = 1.0 - edgeA
  133. let eC = 255.0 * edgeA
  134. let off = rowBase + x * bpp
  135. p[off] = UInt8(min(255, eC + Double(p[off]) * invA))
  136. p[off+1] = UInt8(min(255, eC + Double(p[off+1]) * invA))
  137. p[off+2] = UInt8(min(255, eC + Double(p[off+2]) * invA))
  138. if bpp == 4 {
  139. p[off+3] = UInt8(min(255, edgeA * 255.0 + Double(p[off+3]) * invA))
  140. }
  141. }
  142. }
  143. }
  144. }
  145. }
  146. // Particle System for ArkText
  147. struct ArkTextDot {
  148. var x: Double, y: Double
  149. var targetX: Double, targetY: Double
  150. }
  151. func drawSwiftSplash() {
  152. print("ui_daemon: Animating Splash Screen in 4K Wayland mode...")
  153. // Initialize the Wayland client for the splash screen
  154. splashGraphics = ArkGraphics()
  155. if !splashGraphics.isConnected {
  156. print("ui_daemon: FATAL - Could not connect splash to Wayland server")
  157. return
  158. }
  159. // Extract the raw buffer pointer from ArkGraphics (via memory reflection or a temporary workaround)
  160. // Wait, ArkGraphics doesn't expose `buffer` publicly. Let's make it public!
  161. renderBuffer = splashGraphics.buffer
  162. scrW = splashGraphics.screenWidth
  163. scrH = splashGraphics.screenHeight
  164. lineLen = scrW * bpp
  165. // Scale parameters based on resolution (assuming 1920x1080 baseline)
  166. let scale = Double(scrW) / 1920.0
  167. NUC_R = 28.0 * scale
  168. INNER_R = 120.0 * scale
  169. OUTER_R = 200.0 * scale
  170. ELEC_R = 12.0 * scale
  171. RING_T = 3.0 * scale
  172. let startCx = Double(scrW) / 2.0
  173. let startCy = Double(scrH) / 2.0
  174. // Frame timing via compositor
  175. func emitFrame() {
  176. presentScreen()
  177. }
  178. // Phase 1: Spawn & Accelerate (45 frames)
  179. for frame in 0..<45 {
  180. clearScreen(r: 0, g: 0, b: 0)
  181. let t = Double(frame) / 44.0
  182. let easeOut = 1.0 - pow(1.0 - t, 3.0)
  183. let curInnerR = INNER_R * easeOut
  184. let curOuterR = OUTER_R * easeOut
  185. let curElR = max(1.0, ELEC_R * easeOut)
  186. let ringBright = UInt8(90.0 * easeOut) // Brighter rings
  187. diskAA(renderBuffer, startCx, startCy, NUC_R, 255, 255, 255, 255)
  188. if curInnerR > NUC_R { ringAA(renderBuffer, startCx, startCy, curInnerR, RING_T, ringBright) }
  189. if curOuterR > NUC_R { ringAA(renderBuffer, startCx, startCy, curOuterR, RING_T, ringBright) }
  190. // Quadrupled the rotation speed (ease-in so it smoothly starts spinning)
  191. let angle = pow(t, 2.0) * 8.0
  192. for i in 0..<2 {
  193. let a = angle + Double(i) * Double.pi
  194. diskAA(renderBuffer, startCx + cos(a) * curInnerR, startCy + sin(a) * curInnerR, curElR, 255, 255, 255, 255)
  195. }
  196. for i in 0..<2 {
  197. let a = -angle * 0.7 + Double(i) * Double.pi
  198. diskAA(renderBuffer, startCx + cos(a) * curOuterR, startCy + sin(a) * curOuterR, curElR, 255, 255, 255, 255)
  199. }
  200. emitFrame()
  201. }
  202. // Phase 2: Fast Stable Orbiting (120 frames for multiple full rotations)
  203. for frame in 0..<120 {
  204. clearScreen(r: 0, g: 0, b: 0)
  205. // 8.0 rads from previous + 0.3 rads per frame = fast spinning!
  206. let angle = 8.0 + Double(frame) * 0.3
  207. diskAA(renderBuffer, startCx, startCy, NUC_R, 255, 255, 255, 255)
  208. ringAA(renderBuffer, startCx, startCy, INNER_R, RING_T, 90)
  209. ringAA(renderBuffer, startCx, startCy, OUTER_R, RING_T, 90)
  210. for i in 0..<2 {
  211. let a = angle + Double(i) * Double.pi
  212. diskAA(renderBuffer, startCx + cos(a) * INNER_R, startCy + sin(a) * INNER_R, ELEC_R, 255, 255, 255, 255)
  213. }
  214. for i in 0..<2 {
  215. let a = -angle * 0.7 + Double(i) * Double.pi
  216. diskAA(renderBuffer, startCx + cos(a) * OUTER_R, startCy + sin(a) * OUTER_R, ELEC_R, 255, 255, 255, 255)
  217. }
  218. emitFrame()
  219. }
  220. // Phase 3: Deceleration (60 frames)
  221. let baseAngle = 8.0 + (120.0 * 0.3)
  222. var currentSpeed = 0.3
  223. var currentAngle = baseAngle
  224. for _ in 0..<60 {
  225. clearScreen(r: 0, g: 0, b: 0)
  226. currentSpeed *= 0.93 // Slower decay for smoother stop
  227. currentAngle += currentSpeed
  228. diskAA(renderBuffer, startCx, startCy, NUC_R, 255, 255, 255, 255)
  229. ringAA(renderBuffer, startCx, startCy, INNER_R, RING_T, 90)
  230. ringAA(renderBuffer, startCx, startCy, OUTER_R, RING_T, 90)
  231. for i in 0..<2 {
  232. let a = currentAngle + Double(i) * Double.pi
  233. diskAA(renderBuffer, startCx + cos(a) * INNER_R, startCy + sin(a) * INNER_R, ELEC_R, 255, 255, 255, 255)
  234. }
  235. for i in 0..<2 {
  236. let a = -currentAngle * 0.7 + Double(i) * Double.pi
  237. diskAA(renderBuffer, startCx + cos(a) * OUTER_R, startCy + sin(a) * OUTER_R, ELEC_R, 255, 255, 255, 255)
  238. }
  239. emitFrame()
  240. }
  241. // Phase 4: Slide to Top-Left (40 frames)
  242. let endAngleInner = currentAngle
  243. let endAngleOuter = -currentAngle * 0.7
  244. let targetCx = 180.0 * scale
  245. let targetCy = 180.0 * scale
  246. for frame in 0..<40 {
  247. clearScreen(r: 0, g: 0, b: 0)
  248. let t = Double(frame) / 39.0
  249. let easeOut = 1.0 - pow(1.0 - t, 3.0)
  250. let cx = startCx + (targetCx - startCx) * easeOut
  251. let cy = startCy + (targetCy - startCy) * easeOut
  252. diskAA(renderBuffer, cx, cy, NUC_R, 255, 255, 255, 255)
  253. ringAA(renderBuffer, cx, cy, INNER_R, RING_T, 90)
  254. ringAA(renderBuffer, cx, cy, OUTER_R, RING_T, 90)
  255. for i in 0..<2 {
  256. let a = endAngleInner + Double(i) * Double.pi
  257. diskAA(renderBuffer, cx + cos(a) * INNER_R, cy + sin(a) * INNER_R, ELEC_R, 255, 255, 255, 255)
  258. }
  259. for i in 0..<2 {
  260. let a = endAngleOuter + Double(i) * Double.pi
  261. diskAA(renderBuffer, cx + cos(a) * OUTER_R, cy + sin(a) * OUTER_R, ELEC_R, 255, 255, 255, 255)
  262. }
  263. emitFrame()
  264. }
  265. // Phase 5: Electron Glows Blue (30 frames)
  266. let glowingIndex = 0
  267. let glowingAngle = endAngleInner + Double(glowingIndex) * Double.pi
  268. let glowEx = targetCx + cos(glowingAngle) * INNER_R
  269. let glowEy = targetCy + sin(glowingAngle) * INNER_R
  270. for frame in 0..<30 {
  271. clearScreen(r: 0, g: 0, b: 0)
  272. let t = Double(frame) / 29.0
  273. diskAA(renderBuffer, targetCx, targetCy, NUC_R, 255, 255, 255, 255)
  274. ringAA(renderBuffer, targetCx, targetCy, INNER_R, RING_T, 90)
  275. ringAA(renderBuffer, targetCx, targetCy, OUTER_R, RING_T, 90)
  276. // Non-glowing electron
  277. let a2 = endAngleInner + Double.pi
  278. diskAA(renderBuffer, targetCx + cos(a2) * INNER_R, targetCy + sin(a2) * INNER_R, ELEC_R, 255, 255, 255, 255)
  279. for i in 0..<2 {
  280. let a = endAngleOuter + Double(i) * Double.pi
  281. diskAA(renderBuffer, targetCx + cos(a) * OUTER_R, targetCy + sin(a) * OUTER_R, ELEC_R, 255, 255, 255, 255)
  282. }
  283. // Glowing electron
  284. let glowRad = 20.0 * scale * t
  285. if glowRad > 0 {
  286. // Blue glow aura
  287. diskAA(renderBuffer, glowEx, glowEy, ELEC_R + glowRad, 30, 144, 255, UInt8(180.0 * t))
  288. }
  289. // Core changes to bright blue
  290. let coreR = UInt8(255 - 200 * t)
  291. let coreG = UInt8(255 - 100 * t)
  292. diskAA(renderBuffer, glowEx, glowEy, ELEC_R, coreR, coreG, 255, 255)
  293. emitFrame()
  294. }
  295. // Phase 6: Drop and Roll to "Get Started" (50 frames)
  296. let btnW = 300.0 * scale
  297. let btnH = 80.0 * scale
  298. let btnX = Double(scrW) / 2.0 - btnW / 2.0
  299. let btnY = Double(scrH) - 200.0 * scale - btnH
  300. for frame in 0..<50 {
  301. clearScreen(r: 0, g: 0, b: 0)
  302. let t = Double(frame) / 49.0
  303. let easeIn = t * t
  304. // Draw static atom
  305. diskAA(renderBuffer, targetCx, targetCy, NUC_R, 255, 255, 255, 255)
  306. ringAA(renderBuffer, targetCx, targetCy, INNER_R, RING_T, 90)
  307. ringAA(renderBuffer, targetCx, targetCy, OUTER_R, RING_T, 90)
  308. let a2 = endAngleInner + Double.pi
  309. diskAA(renderBuffer, targetCx + cos(a2) * INNER_R, targetCy + sin(a2) * INNER_R, ELEC_R, 255, 255, 255, 255)
  310. for i in 0..<2 {
  311. let a = endAngleOuter + Double(i) * Double.pi
  312. diskAA(renderBuffer, targetCx + cos(a) * OUTER_R, targetCy + sin(a) * OUTER_R, ELEC_R, 255, 255, 255, 255)
  313. }
  314. // Falling & morphing electron
  315. let curEx = glowEx + (btnX + btnW/2 - glowEx) * t
  316. // Bounce effect for Y
  317. let bounceT = abs(sin(t * Double.pi * 1.5)) * (1.0 - t)
  318. let curEy = glowEy + (btnY + btnH/2 - glowEy) * easeIn - (100.0 * scale * bounceT)
  319. // Morph from circle to rounded rect
  320. let curW = (ELEC_R * 2.0) + (btnW - (ELEC_R * 2.0)) * t
  321. let curH = (ELEC_R * 2.0) + (btnH - (ELEC_R * 2.0)) * t
  322. let curRad = Int((ELEC_R) + (Double(btnH/2) - ELEC_R) * t)
  323. drawRoundedRect(x: Int(curEx - curW/2), y: Int(curEy - curH/2), w: Int(curW), h: Int(curH), r: 30, g: 144, b: 255, a: 255, radius: curRad)
  324. emitFrame()
  325. }
  326. // Phase 7: White Dots Assemble "Welcome to ARK-OS!" (60 frames)
  327. // Create text points by sampling a grid inside the text area
  328. var dots: [ArkTextDot] = []
  329. let textW = 800.0 * scale
  330. let textH = 100.0 * scale
  331. let textX = Double(scrW)/2.0 - textW/2.0
  332. let textY = Double(scrH)/2.0 - textH/2.0
  333. // Create some random dots that represent "text"
  334. for _ in 0..<2000 {
  335. let ty = Double.random(in: 0..<textH)
  336. let tx = Double.random(in: 0..<textW)
  337. // Just rough bounds to look like text assembling
  338. if sin(tx * 0.05) * cos(ty * 0.1) > 0.2 {
  339. let spawnX = (Double.random(in: 0...1) > 0.5) ? -200.0 : Double(scrW) + 200.0
  340. let spawnY = Double.random(in: 0..<Double(scrH))
  341. dots.append(ArkTextDot(x: spawnX, y: spawnY, targetX: textX + tx, targetY: textY + ty))
  342. }
  343. }
  344. for frame in 0..<60 {
  345. clearScreen(r: 0, g: 0, b: 0)
  346. let t = Double(frame) / 59.0
  347. let _ = 1.0 - pow(1.0 - t, 3.0)
  348. // Static background
  349. diskAA(renderBuffer, targetCx, targetCy, NUC_R, 255, 255, 255, 255)
  350. ringAA(renderBuffer, targetCx, targetCy, INNER_R, RING_T, 90)
  351. ringAA(renderBuffer, targetCx, targetCy, OUTER_R, RING_T, 90)
  352. let a2 = endAngleInner + Double.pi
  353. diskAA(renderBuffer, targetCx + cos(a2) * INNER_R, targetCy + sin(a2) * INNER_R, ELEC_R, 255, 255, 255, 255)
  354. for i in 0..<2 {
  355. let a = endAngleOuter + Double(i) * Double.pi
  356. diskAA(renderBuffer, targetCx + cos(a) * OUTER_R, targetCy + sin(a) * OUTER_R, ELEC_R, 255, 255, 255, 255)
  357. }
  358. drawRoundedRect(x: Int(btnX), y: Int(btnY), w: Int(btnW), h: Int(btnH), r: 30, g: 144, b: 255, a: 255, radius: Int(btnH/2))
  359. // Assemble dots
  360. for i in 0..<dots.count {
  361. dots[i].x = dots[i].x + (dots[i].targetX - dots[i].x) * 0.1
  362. dots[i].y = dots[i].y + (dots[i].targetY - dots[i].y) * 0.1
  363. let px = Int(dots[i].x)
  364. let py = Int(dots[i].y)
  365. if px >= 0 && px < scrW && py >= 0 && py < scrH {
  366. let off = py * lineLen + px * bpp
  367. renderBuffer[off] = 255
  368. renderBuffer[off+1] = 255
  369. renderBuffer[off+2] = 255
  370. if bpp == 4 { renderBuffer[off+3] = 255 }
  371. // Make dots slightly larger for 4K
  372. if scale > 1.5 {
  373. if px+1 < scrW {
  374. renderBuffer[off+bpp] = 255; renderBuffer[off+bpp+1] = 255; renderBuffer[off+bpp+2] = 255; if bpp == 4 { renderBuffer[off+bpp+3] = 255 }
  375. }
  376. if py+1 < scrH {
  377. let off2 = (py+1) * lineLen + px * bpp
  378. renderBuffer[off2] = 255; renderBuffer[off2+1] = 255; renderBuffer[off2+2] = 255; if bpp == 4 { renderBuffer[off2+3] = 255 }
  379. }
  380. }
  381. }
  382. }
  383. emitFrame()
  384. }
  385. }