ArkGraphicsBackend.swift 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. //
  2. // Copyright 2026 Aarav Ravindra Kharade
  3. //
  4. import CSystem
  5. /// A custom widget node tree for SwiftCrossUI over ArkGraphics IPC
  6. public class ArkGraphicsWidget {
  7. public var children: [ArkGraphicsWidget] = []
  8. public var x: Int = 0
  9. public var y: Int = 0
  10. public var width: Int = 0
  11. public var height: Int = 0
  12. public init() {}
  13. }
  14. public class ArkGraphicsWindow {
  15. public var rootWidget: ArkGraphicsWidget?
  16. public var title: String = ""
  17. public init() {}
  18. }
  19. /// The Backend bridging SwiftCrossUI to ArkGraphics IPC
  20. public class ArkGraphicsBackend: BackendFeatures.BaseStubs {
  21. public struct Widget {
  22. var node: ArkGraphicsWidget
  23. }
  24. public struct Window {
  25. var node: ArkGraphicsWindow
  26. }
  27. public required init() {}
  28. public var deviceClass: DeviceClass { .desktop }
  29. public static func earlySetup() {}
  30. public func runMainLoop(_ callback: @escaping @MainActor () -> Void) {
  31. // Setup ArkGraphics IPC
  32. let graphics = ArkGraphics()
  33. if !graphics.isConnected {
  34. print("ArkGraphicsBackend: FATAL - Cannot connect to display daemon")
  35. return
  36. }
  37. callback()
  38. // Main run loop
  39. while true {
  40. usleep(16000) // ~60fps
  41. // Poll for inputs, trigger UI redraws, etc
  42. }
  43. }
  44. public nonisolated func runInMainThread(action: @escaping @MainActor () -> Void) {
  45. // Run on the single main thread
  46. Task { @MainActor in
  47. action()
  48. }
  49. }
  50. public func computeRootEnvironment(defaultEnvironment: EnvironmentValues) -> EnvironmentValues {
  51. return defaultEnvironment
  52. }
  53. public func setRootEnvironmentChangeHandler(to action: @escaping @Sendable @MainActor () -> Void) {}
  54. // - CoreWindowing -
  55. public func createWindow(withDefaultSize defaultSize: SIMD2<Int>?, id: String) -> Window {
  56. return Window(node: ArkGraphicsWindow())
  57. }
  58. public func updateWindow(_ window: Window, title: String) {
  59. window.node.title = title
  60. }
  61. public func updateWindow(_ window: Window, resizability: WindowResizability) {}
  62. public func setChild(ofWindow window: Window, to child: Widget) {
  63. window.node.rootWidget = child.node
  64. }
  65. public func showWindow(_ window: Window) {}
  66. // - GenericContainers -
  67. public func createContainer() -> Widget {
  68. return Widget(node: ArkGraphicsWidget())
  69. }
  70. public func addChild(_ child: Widget, to container: Widget) {
  71. container.node.children.append(child.node)
  72. }
  73. public func setChildren(_ children: [Widget], of container: Widget) {
  74. container.node.children = children.map { $0.node }
  75. }
  76. public func removeChild(_ child: Widget, from container: Widget) {
  77. container.node.children.removeAll(where: { $0 === child.node })
  78. }
  79. public func removeAllChildren(of container: Widget) {
  80. container.node.children.removeAll()
  81. }
  82. public func insert(_ child: Widget, into container: Widget, at index: Int) {
  83. container.node.children.insert(child.node, at: index)
  84. }
  85. public func swap(childAt firstIndex: Int, withChildAt secondIndex: Int, in container: Widget) {
  86. container.node.children.swapAt(firstIndex, secondIndex)
  87. }
  88. public func setPosition(ofChildAt index: Int, in container: Widget, to position: SIMD2<Int>) {
  89. container.node.children[index].x = position.x
  90. container.node.children[index].y = position.y
  91. }
  92. public func remove(childAt index: Int, from container: Widget) {
  93. container.node.children.remove(at: index)
  94. }
  95. // - TextViews -
  96. public func createTextView() -> Widget {
  97. return Widget(node: ArkGraphicsWidget())
  98. }
  99. public func updateTextView(_ textView: Widget, content: String, environment: EnvironmentValues) {}
  100. public func size(of text: String, whenDisplayedIn widget: Widget, proposedWidth: Int?, proposedHeight: Int?, environment: EnvironmentValues) -> SIMD2<Int> {
  101. return SIMD2<Int>(text.count * 8, 16)
  102. }
  103. // - Buttons -
  104. public func createSimpleButton() -> Widget {
  105. return Widget(node: ArkGraphicsWidget())
  106. }
  107. public func updateSimpleButton(_ button: Widget, label: String, environment: EnvironmentValues, action: @escaping () -> Void) {}
  108. public func createButton(wrapping: Widget) -> Widget {
  109. return Widget(node: ArkGraphicsWidget())
  110. }
  111. public func updateButton(_ button: Widget, environment: EnvironmentValues, action: @escaping () -> Void) {}
  112. public func buttonPadding(in environment: EnvironmentValues) -> SIMD2<Int> {
  113. return SIMD2<Int>(8, 8)
  114. }
  115. public func defaultButtonStyle() -> ButtonStyle {
  116. return .bordered
  117. }
  118. public var scrollBarWidth: Int { return 10 }
  119. public var requiresImageUpdateOnScaleFactorChange: Bool { return false }
  120. public var requiresToggleSwitchSpacer: Bool { return false }
  121. public var supportedPickerStyles: [BackendPickerStyle] { return [] }
  122. // Windowing extra methods
  123. public func show(window: Window) {}
  124. public func setTitle(ofWindow window: Window, to title: String) {}
  125. public func updateWindow(_ window: Window, environment: EnvironmentValues) {}
  126. public func size(ofWindow window: Window) -> SIMD2<Int> { return SIMD2<Int>(1024, 768) }
  127. public func setSize(ofWindow window: Window, to newSize: SIMD2<Int>) {}
  128. public func setSizeLimits(ofWindow window: Window, minimum minimumSize: SIMD2<Int>, maximum maximumSize: SIMD2<Int>?) {}
  129. public func isWindowProgrammaticallyResizable(_ window: Window) -> Bool { return false }
  130. public func setResizeHandler(ofWindow window: Window, to action: @escaping (SIMD2<Int>) -> Void) {}
  131. public func activate(window: Window) {}
  132. public func computeWindowEnvironment(window: Window, rootEnvironment: EnvironmentValues) -> EnvironmentValues { return rootEnvironment }
  133. public func setWindowEnvironmentChangeHandler(of window: Window, to action: @escaping @Sendable @MainActor () -> Void) {}
  134. public var defaultPaddingAmount: Int { return 10 }
  135. public func naturalSize(of widget: Widget) -> SIMD2<Int> { return SIMD2<Int>(0, 0) }
  136. public func setSize(of widget: Widget, to size: SIMD2<Int>) {
  137. widget.node.width = size.x
  138. widget.node.height = size.y
  139. }
  140. public func show(widget: Widget) {}
  141. public var supportsMultipleWindows: Bool { return false }
  142. public var canOverrideWindowColorScheme: Bool { return false }
  143. public var restoresWindowFrames: Bool { return false }
  144. }