1
0

ArkGraphics.swift 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. //
  2. // Copyright 2026 Aarav Ravindra Kharade
  3. //
  4. import CSystem
  5. import CWayland
  6. @_silgen_name("ark_wayland_client_init")
  7. func c_ark_wayland_client_init() -> Bool
  8. @_silgen_name("ark_wayland_client_shutdown")
  9. func c_ark_wayland_client_shutdown()
  10. @_silgen_name("ark_wayland_client_create_surface")
  11. func c_ark_wayland_client_create_surface(_ width: Int32, _ height: Int32) -> UnsafeMutablePointer<UInt8>?
  12. @_silgen_name("ark_wayland_client_commit")
  13. func c_ark_wayland_client_commit()
  14. /// ArkGraphics — Display Client Library
  15. /// Connects to the ArkCompositor (Wayland Server) to render UI.
  16. public class ArkGraphics {
  17. public var screenWidth: Int = 1920
  18. public var screenHeight: Int = 1080
  19. public var buffer: UnsafeMutablePointer<UInt8>?
  20. public private(set) var isConnected: Bool = false
  21. public init() {
  22. // For now, fallback to a standard 1080p resolution unless /tmp/screen_size.txt exists
  23. if let str = readFileContents("/tmp/screen_size.txt") {
  24. var w = 0
  25. var h = 0
  26. var parsingW = true
  27. for char in str {
  28. if char == " " {
  29. parsingW = false
  30. } else if let num = char.wholeNumberValue {
  31. if parsingW {
  32. w = w * 10 + num
  33. } else {
  34. h = h * 10 + num
  35. }
  36. }
  37. }
  38. if w > 0 && h > 0 {
  39. self.screenWidth = w
  40. self.screenHeight = h
  41. }
  42. }
  43. let maxRetries = 10
  44. for _ in 1...maxRetries {
  45. if c_ark_wayland_client_init() {
  46. isConnected = true
  47. break
  48. }
  49. usleep(500_000)
  50. }
  51. if isConnected {
  52. buffer = c_ark_wayland_client_create_surface(Int32(screenWidth), Int32(screenHeight))
  53. if buffer == nil {
  54. print("ArkGraphics: FATAL - Failed to create Wayland surface")
  55. }
  56. } else {
  57. print("ArkGraphics: WARNING - Failed to connect to Wayland server")
  58. }
  59. }
  60. deinit {
  61. if isConnected {
  62. c_ark_wayland_client_shutdown()
  63. }
  64. }
  65. @inline(__always)
  66. private func toTopLeftY(y: Int, h: Int) -> Int {
  67. return screenHeight - y - h
  68. }
  69. // - Rendering Commands -
  70. public func fillRectRGB(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8) {
  71. fillRectRGBA(x: x, y: y, w: w, h: h, r: r, g: g, b: b, a: 255)
  72. }
  73. public func fillRectRGBA(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8, a: UInt8) {
  74. guard let p = buffer else { return }
  75. let sy = toTopLeftY(y: y, h: h)
  76. let y0 = max(0, sy)
  77. let y1 = min(screenHeight, sy + h)
  78. let x0 = max(0, x)
  79. let x1 = min(screenWidth, x + w)
  80. for cy in y0..<y1 {
  81. let rowBase = cy * screenWidth * 4
  82. for cx in x0..<x1 {
  83. let off = rowBase + cx * 4
  84. if a == 255 {
  85. p[off] = b
  86. p[off+1] = g
  87. p[off+2] = r
  88. p[off+3] = 255
  89. } else {
  90. let curA = Double(p[off+3]) / 255.0
  91. let fVal = Double(a) / 255.0
  92. let newA = curA + fVal * (1.0 - curA)
  93. if newA > 0 {
  94. p[off] = UInt8(Double(b) * fVal + Double(p[off]) * curA * (1.0 - fVal))
  95. p[off+1] = UInt8(Double(g) * fVal + Double(p[off+1]) * curA * (1.0 - fVal))
  96. p[off+2] = UInt8(Double(r) * fVal + Double(p[off+2]) * curA * (1.0 - fVal))
  97. p[off+3] = UInt8(newA * 255.0)
  98. }
  99. }
  100. }
  101. }
  102. }
  103. public func fillRoundedRectRGB(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8, radius: Int) {
  104. fillRoundedRectRGBA(x: x, y: y, w: w, h: h, r: r, g: g, b: b, a: 255, radius: radius)
  105. }
  106. public func fillRoundedRectRGBA(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8, a: UInt8, radius: Int) {
  107. guard let p = buffer else { return }
  108. let sy = toTopLeftY(y: y, h: h)
  109. let y0 = max(0, sy)
  110. let y1 = min(screenHeight, sy + h)
  111. let x0 = max(0, x)
  112. let x1 = min(screenWidth, x + w)
  113. let radSq = Double(radius * radius)
  114. for cy in y0..<y1 {
  115. let rowBase = cy * screenWidth * 4
  116. for cx in x0..<x1 {
  117. var draw = true
  118. if radius > 0 {
  119. // Check corners
  120. let innerX0 = x + radius
  121. let innerX1 = x + w - radius
  122. let innerY0 = sy + radius
  123. let innerY1 = sy + h - radius
  124. var dx = 0
  125. var dy = 0
  126. if cx < innerX0 { dx = innerX0 - cx }
  127. else if cx > innerX1 { dx = cx - innerX1 }
  128. if cy < innerY0 { dy = innerY0 - cy }
  129. else if cy > innerY1 { dy = cy - innerY1 }
  130. if dx > 0 && dy > 0 {
  131. if Double(dx*dx + dy*dy) > radSq {
  132. draw = false
  133. }
  134. }
  135. }
  136. if draw {
  137. let off = rowBase + cx * 4
  138. p[off] = b
  139. p[off+1] = g
  140. p[off+2] = r
  141. p[off+3] = a
  142. }
  143. }
  144. }
  145. }
  146. public func clear(r: UInt8, g: UInt8, b: UInt8) {
  147. guard let p = buffer else { return }
  148. let count = screenWidth * screenHeight * 4
  149. for i in stride(from: 0, to: count, by: 4) {
  150. p[i] = b
  151. p[i+1] = g
  152. p[i+2] = r
  153. p[i+3] = 255
  154. }
  155. }
  156. public func pan() {
  157. if isConnected {
  158. c_ark_wayland_client_commit()
  159. }
  160. }
  161. public func drawHLine(x: Int, y: Int, w: Int, r: UInt8, g: UInt8, b: UInt8) {
  162. fillRectRGB(x: x, y: y, w: w, h: 1, r: r, g: g, b: b)
  163. }
  164. public func drawVLine(x: Int, y: Int, h: Int, r: UInt8, g: UInt8, b: UInt8) {
  165. fillRectRGB(x: x, y: y, w: 1, h: h, r: r, g: g, b: b)
  166. }
  167. public func drawRectOutline(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8, thickness: Int = 1) {
  168. fillRectRGB(x: x, y: y, w: w, h: thickness, r: r, g: g, b: b)
  169. fillRectRGB(x: x, y: y + h - thickness, w: w, h: thickness, r: r, g: g, b: b)
  170. fillRectRGB(x: x, y: y, w: thickness, h: h, r: r, g: g, b: b)
  171. fillRectRGB(x: x + w - thickness, y: y, w: thickness, h: h, r: r, g: g, b: b)
  172. }
  173. }