// // Copyright 2026 Aarav Ravindra Kharade // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // import Foundation #if canImport(Glibc) import Glibc #endif // - // ArkGraphics — Display Client Library // - // Client-side library that connects to the ArkOS Display Daemon // via /dev/display.sock and sends rendering commands. // // This is the rendering API used by all ArkOS UI applications. // Commands are sent as packed binary structs over a Unix domain // socket for minimal overhead. // // Usage: // let gfx = ArkGraphics() // gfx.clear(r: 30, g: 30, b: 35) // gfx.fillRect(x: 100, y: 100, w: 200, h: 50, r: 0, g: 120, b: 215) // gfx.drawLine(x1: 10, y1: 10, x2: 200, y2: 200, r: 255, g: 255, b: 255) // gfx.pan() // Present to screen // // - // - Wire Protocol - /// Packed command structure sent to the display daemon. /// Total size: 25 bytes (1 + 5*4 + 4*1). private struct DisplayCommand { var cmd: UInt8 // Command ID (1=fillRect, 2=pan, 3=clear, 4=fillRoundedRect) var x: Int32 // X coordinate (or 0 for non-spatial commands) var y: Int32 // Y coordinate var w: Int32 // Width var h: Int32 // Height var r: UInt8 // Red (0–255) var g: UInt8 // Green (0–255) var b: UInt8 // Blue (0–255) var a: UInt8 // Alpha (0–255) var radius: Int32 // Border radius } // - Graphics Client - /// Client-side handle to the display daemon. Manages the socket /// connection and provides high-level drawing primitives. // C bridge to bypass varargs issues in Swift's ioctl binding @_silgen_name("ioctl") func ioctl_fb(_ fd: Int32, _ request: UInt, _ info: UnsafeMutableRawPointer) -> Int32 public class ArkGraphics { /// File descriptor for the Unix domain socket connection. private var fd: Int32 = -1 /// Path to the display daemon socket. private let socketPath = "/dev/display.sock" /// Maximum connection retry attempts on initialization. private let maxRetries = 10 private let retryDelay: useconds_t = 500_000 // 500ms public var screenWidth: Int = 1024 public var screenHeight: Int = 768 /// Creates a new graphics client and connects to the display daemon. /// Retries up to 10 times with 500ms delay (total 5s) to allow the /// daemon time to start during boot. public init() { // Query real resolution from fb0 let fb_fd = open("/dev/fb0", O_RDONLY) if fb_fd >= 0 { // Memory layout of fb_var_screeninfo (ABI compatible with Linux) struct fb_var_screeninfo { var xres: UInt32 = 0 var yres: UInt32 = 0 var xres_virtual: UInt32 = 0 var yres_virtual: UInt32 = 0 var xoffset: UInt32 = 0 var yoffset: UInt32 = 0 var bits_per_pixel: UInt32 = 0 var grayscale: UInt32 = 0 var red: (UInt32, UInt32, UInt32) = (0,0,0) var green: (UInt32, UInt32, UInt32) = (0,0,0) var blue: (UInt32, UInt32, UInt32) = (0,0,0) var transp: (UInt32, UInt32, UInt32) = (0,0,0) var nonstd: UInt32 = 0 var activate: UInt32 = 0 var height: UInt32 = 0 var width: UInt32 = 0 var accel_flags: UInt32 = 0 var pixclock: UInt32 = 0 var left_margin: UInt32 = 0 var right_margin: UInt32 = 0 var upper_margin: UInt32 = 0 var lower_margin: UInt32 = 0 var hsync_len: UInt32 = 0 var vsync_len: UInt32 = 0 var sync: UInt32 = 0 var vmode: UInt32 = 0 var reserved: (UInt32, UInt32, UInt32, UInt32, UInt32, UInt32) = (0, 0, 0, 0, 0, 0) } var vinfo = fb_var_screeninfo() let FBIOGET_VSCREENINFO: UInt = 0x4600 if ioctl_fb(fb_fd, FBIOGET_VSCREENINFO, &vinfo) >= 0 { self.screenWidth = Int(vinfo.xres) self.screenHeight = Int(vinfo.yres) } close(fb_fd) } for attempt in 1...maxRetries { if tryConnect() { return } if attempt < maxRetries { usleep(retryDelay) } } print("ArkGraphics: WARNING — Failed to connect to display daemon after \(maxRetries) attempts") } deinit { if fd >= 0 { close(fd) } } // - Connection Management - /// Attempts to connect to the display daemon socket. /// Returns true on success, false on failure. private func tryConnect() -> Bool { let sock = socket(AF_UNIX, Int32(SOCK_STREAM.rawValue), 0) if sock < 0 { return false } var addr = sockaddr_un() addr.sun_family = sa_family_t(AF_UNIX) let pathBytes = socketPath.utf8CString withUnsafeMutablePointer(to: &addr.sun_path) { sunPathPtr in let rawPtr = UnsafeMutableRawPointer(sunPathPtr).assumingMemoryBound(to: CChar.self) for i in 0...size let connectResult = withUnsafePointer(to: &addr) { addrPtr in addrPtr.withMemoryRebound(to: sockaddr.self, capacity: 1) { saPtr in connect(sock, saPtr, socklen_t(addrSize)) } } if connectResult < 0 { close(sock) return false } self.fd = sock return true } /// Returns true if connected to the display daemon. public var isConnected: Bool { return fd >= 0 } // - Rendering Commands - /// Fills a rectangle with a solid RGB color. /// /// - Parameters: /// - x: Left edge in pixels /// - y: Top edge in pixels /// - w: Width in pixels /// - h: Height in pixels /// - r: Red component (0–255) /// - g: Green component (0–255) /// - b: Blue component (0–255) public func fillRectRGB(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8) { sendCommand(DisplayCommand( cmd: 1, x: Int32(x), y: Int32(y), w: Int32(w), h: Int32(h), r: r, g: g, b: b, a: 255, radius: 0 )) } /// Fills a rectangle with an RGBA color, blending it with the background. public func fillRectRGBA(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8, a: UInt8) { sendCommand(DisplayCommand( cmd: 1, x: Int32(x), y: Int32(y), w: Int32(w), h: Int32(h), r: r, g: g, b: b, a: a, radius: 0 )) } /// Fills a rounded rectangle with a solid RGB color. public func fillRoundedRectRGB(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8, radius: Int) { sendCommand(DisplayCommand( cmd: 4, x: Int32(x), y: Int32(y), w: Int32(w), h: Int32(h), r: r, g: g, b: b, a: 255, radius: Int32(radius) )) } /// Fills a rounded rectangle with an RGBA color. public func fillRoundedRectRGBA(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8, a: UInt8, radius: Int) { sendCommand(DisplayCommand( cmd: 4, x: Int32(x), y: Int32(y), w: Int32(w), h: Int32(h), r: r, g: g, b: b, a: a, radius: Int32(radius) )) } /// Clears the entire screen to a solid RGB color. public func clear(r: UInt8, g: UInt8, b: UInt8) { sendCommand(DisplayCommand( cmd: 3, x: 0, y: 0, w: 0, h: 0, r: r, g: g, b: b, a: 255, radius: 0 )) } /// Presents the current back buffer to the screen (page flip). /// Must be called after all drawing commands to make them visible. public func pan() { sendCommand(DisplayCommand( cmd: 2, x: 0, y: 0, w: 0, h: 0, r: 0, g: 0, b: 0, a: 0, radius: 0 )) } /// Draws a horizontal line of 1 pixel height. 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) } /// Draws a vertical line of 1 pixel width. 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) } /// Draws a rectangle outline (border only, no fill). /// /// - Parameters: /// - thickness: Border width in pixels (default 1) public func drawRectOutline(x: Int, y: Int, w: Int, h: Int, r: UInt8, g: UInt8, b: UInt8, thickness: Int = 1) { // Top edge fillRectRGB(x: x, y: y, w: w, h: thickness, r: r, g: g, b: b) // Bottom edge fillRectRGB(x: x, y: y + h - thickness, w: w, h: thickness, r: r, g: g, b: b) // Left edge fillRectRGB(x: x, y: y, w: thickness, h: h, r: r, g: g, b: b) // Right edge fillRectRGB(x: x + w - thickness, y: y, w: thickness, h: h, r: r, g: g, b: b) } // - Internal Transport - /// Sends a packed command struct to the display daemon. private func sendCommand(_ cmd: DisplayCommand) { guard fd >= 0 else { return } var buffer = [UInt8](repeating: 0, count: 25) buffer[0] = cmd.cmd var x = cmd.x var y = cmd.y var w = cmd.w var h = cmd.h var radius = cmd.radius buffer.withUnsafeMutableBufferPointer { ptr in guard let baseAddress = ptr.baseAddress else { return } memcpy(baseAddress + 1, &x, 4) memcpy(baseAddress + 5, &y, 4) memcpy(baseAddress + 9, &w, 4) memcpy(baseAddress + 13, &h, 4) memcpy(baseAddress + 21, &radius, 4) } buffer[17] = cmd.r buffer[18] = cmd.g buffer[19] = cmd.b buffer[20] = cmd.a buffer.withUnsafeBufferPointer { ptr in var totalWritten = 0 while totalWritten < 25 { let written = write(fd, ptr.baseAddress! + totalWritten, 25 - totalWritten) if written <= 0 { break } totalWritten += written } } } }