| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202 |
- //
- // Copyright 2026 Aarav Ravindra Kharade
- //
- import CSystem
- import CWayland
- @_silgen_name("ark_wayland_client_init")
- func c_ark_wayland_client_init() -> Bool
- @_silgen_name("ark_wayland_client_shutdown")
- func c_ark_wayland_client_shutdown()
- @_silgen_name("ark_wayland_client_create_surface")
- func c_ark_wayland_client_create_surface(_ width: Int32, _ height: Int32) -> UnsafeMutablePointer<UInt8>?
- @_silgen_name("ark_wayland_client_commit")
- func c_ark_wayland_client_commit()
- /// ArkGraphics — Display Client Library
- /// Connects to the ArkCompositor (Wayland Server) to render UI.
- public class ArkGraphics {
- public var screenWidth: Int = 1920
- public var screenHeight: Int = 1080
-
- public var buffer: UnsafeMutablePointer<UInt8>?
- public private(set) var isConnected: Bool = false
-
- public init() {
- // For now, fallback to a standard 1080p resolution unless /tmp/screen_size.txt exists
- if let str = readFileContents("/tmp/screen_size.txt") {
- var w = 0
- var h = 0
- var parsingW = true
- for char in str {
- if char == " " {
- parsingW = false
- } else if let num = char.wholeNumberValue {
- if parsingW {
- w = w * 10 + num
- } else {
- h = h * 10 + num
- }
- }
- }
- if w > 0 && h > 0 {
- self.screenWidth = w
- self.screenHeight = h
- }
- }
-
- let maxRetries = 10
- for _ in 1...maxRetries {
- if c_ark_wayland_client_init() {
- isConnected = true
- break
- }
- usleep(500_000)
- }
-
- if isConnected {
- buffer = c_ark_wayland_client_create_surface(Int32(screenWidth), Int32(screenHeight))
- if buffer == nil {
- print("ArkGraphics: FATAL - Failed to create Wayland surface")
- }
- } else {
- print("ArkGraphics: WARNING - Failed to connect to Wayland server")
- }
- }
-
- deinit {
- if isConnected {
- c_ark_wayland_client_shutdown()
- }
- }
-
- @inline(__always)
- private func toTopLeftY(y: Int, h: Int) -> Int {
- return screenHeight - y - h
- }
-
- // - Rendering Commands -
-
- public func fillRectRGB(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8) {
- fillRectRGBA(x: x, y: y, w: w, h: h, r: r, g: g, b: b, a: 255)
- }
-
- public func fillRectRGBA(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8, a: UInt8) {
- guard let p = buffer else { return }
- let sy = toTopLeftY(y: y, h: h)
-
- let y0 = max(0, sy)
- let y1 = min(screenHeight, sy + h)
- let x0 = max(0, x)
- let x1 = min(screenWidth, x + w)
-
- for cy in y0..<y1 {
- let rowBase = cy * screenWidth * 4
- for cx in x0..<x1 {
- let off = rowBase + cx * 4
- if a == 255 {
- p[off] = b
- p[off+1] = g
- p[off+2] = r
- p[off+3] = 255
- } else {
- let curA = Double(p[off+3]) / 255.0
- let fVal = Double(a) / 255.0
- let newA = curA + fVal * (1.0 - curA)
- if newA > 0 {
- p[off] = UInt8(Double(b) * fVal + Double(p[off]) * curA * (1.0 - fVal))
- p[off+1] = UInt8(Double(g) * fVal + Double(p[off+1]) * curA * (1.0 - fVal))
- p[off+2] = UInt8(Double(r) * fVal + Double(p[off+2]) * curA * (1.0 - fVal))
- p[off+3] = UInt8(newA * 255.0)
- }
- }
- }
- }
- }
-
- public func fillRoundedRectRGB(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8, radius: Int) {
- fillRoundedRectRGBA(x: x, y: y, w: w, h: h, r: r, g: g, b: b, a: 255, radius: radius)
- }
-
- public func fillRoundedRectRGBA(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8, a: UInt8, radius: Int) {
- guard let p = buffer else { return }
- let sy = toTopLeftY(y: y, h: h)
-
- let y0 = max(0, sy)
- let y1 = min(screenHeight, sy + h)
- let x0 = max(0, x)
- let x1 = min(screenWidth, x + w)
- let radSq = Double(radius * radius)
-
- for cy in y0..<y1 {
- let rowBase = cy * screenWidth * 4
- for cx in x0..<x1 {
- var draw = true
- if radius > 0 {
- // Check corners
- let innerX0 = x + radius
- let innerX1 = x + w - radius
- let innerY0 = sy + radius
- let innerY1 = sy + h - radius
-
- var dx = 0
- var dy = 0
- if cx < innerX0 { dx = innerX0 - cx }
- else if cx > innerX1 { dx = cx - innerX1 }
- if cy < innerY0 { dy = innerY0 - cy }
- else if cy > innerY1 { dy = cy - innerY1 }
-
- if dx > 0 && dy > 0 {
- if Double(dx*dx + dy*dy) > radSq {
- draw = false
- }
- }
- }
-
- if draw {
- let off = rowBase + cx * 4
- p[off] = b
- p[off+1] = g
- p[off+2] = r
- p[off+3] = a
- }
- }
- }
- }
-
- public func clear(r: UInt8, g: UInt8, b: UInt8) {
- guard let p = buffer else { return }
- let count = screenWidth * screenHeight * 4
- for i in stride(from: 0, to: count, by: 4) {
- p[i] = b
- p[i+1] = g
- p[i+2] = r
- p[i+3] = 255
- }
- }
-
- public func pan() {
- if isConnected {
- c_ark_wayland_client_commit()
- }
- }
-
- public func drawHLine(x: Int, y: Int, w: Int, r: UInt8, g: UInt8, b: UInt8) {
- fillRectRGB(x: x, y: y, w: w, h: 1, r: r, g: g, b: b)
- }
-
- public func drawVLine(x: Int, y: Int, h: Int, r: UInt8, g: UInt8, b: UInt8) {
- fillRectRGB(x: x, y: y, w: 1, h: h, r: r, g: g, b: b)
- }
-
- public func drawRectOutline(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8, thickness: Int = 1) {
- fillRectRGB(x: x, y: y, w: w, h: thickness, r: r, g: g, b: b)
- fillRectRGB(x: x, y: y + h - thickness, w: w, h: thickness, r: r, g: g, b: b)
- fillRectRGB(x: x, y: y, w: thickness, h: h, r: r, g: g, b: b)
- fillRectRGB(x: x + w - thickness, y: y, w: thickness, h: h, r: r, g: g, b: b)
- }
- }
|