InputService.swift 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. //
  2. // Copyright 2026 Aarav Ravindra Kharade
  3. //
  4. // Licensed under the Apache License, Version 2.0 (the "License");
  5. // you may not use this file except in compliance with the License.
  6. // You may obtain a copy of the License at
  7. //
  8. // http://www.apache.org/licenses/LICENSE-2.0
  9. //
  10. // Unless required by applicable law or agreed to in writing, software
  11. // distributed under the License is distributed on an "AS IS" BASIS,
  12. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. // See the License for the specific language governing permissions and
  14. // limitations under the License.
  15. //
  16. import Foundation
  17. #if canImport(Glibc)
  18. import Glibc
  19. #endif
  20. // ── Linux input_event struct (64-bit) ─────────────────────────────
  21. struct input_event {
  22. var time_sec: Int // 8 bytes
  23. var time_usec: Int // 8 bytes
  24. var type: UInt16 // 2 bytes
  25. var code: UInt16 // 2 bytes
  26. var value: Int32 // 4 bytes
  27. }
  28. // Event Types
  29. let EV_KEY: UInt16 = 1
  30. let EV_REL: UInt16 = 2
  31. let EV_ABS: UInt16 = 3
  32. // Rel Codes
  33. let REL_X: UInt16 = 0
  34. let REL_Y: UInt16 = 1
  35. // Abs Codes
  36. let ABS_X: UInt16 = 0
  37. let ABS_Y: UInt16 = 1
  38. // Btn Codes
  39. let BTN_LEFT: UInt16 = 0x110
  40. let BTN_RIGHT: UInt16 = 0x111
  41. // ── ArkOS Input Protocol ──────────────────────────────────────────
  42. // Sent over /dev/input.sock
  43. // Wire format: 8 bytes per event
  44. // [type: UInt8][padding: UInt8][code: UInt16][value: Int32]
  45. struct ArkInputEvent {
  46. var type: UInt8
  47. var padding: UInt8 = 0
  48. var code: UInt16
  49. var value: Int32
  50. }
  51. public struct InputService {
  52. private static let inputQueue = DispatchQueue(label: "ark.system.input", attributes: .concurrent)
  53. private static var serverFd: Int32 = -1
  54. private static var clients: [Int32] = []
  55. private static let lock = NSLock()
  56. public static func start() {
  57. print("arkrt: InputService starting...")
  58. // Setup Unix Domain Socket server for /dev/input.sock
  59. serverFd = socket(AF_UNIX, Int32(SOCK_STREAM.rawValue), 0)
  60. if serverFd >= 0 {
  61. let socketPath = "/dev/input.sock"
  62. unlink(socketPath)
  63. var addr = sockaddr_un()
  64. addr.sun_family = sa_family_t(AF_UNIX)
  65. let pathBytes = socketPath.utf8CString
  66. withUnsafeMutablePointer(to: &addr.sun_path) { sunPathPtr in
  67. let rawPtr = UnsafeMutableRawPointer(sunPathPtr).assumingMemoryBound(to: CChar.self)
  68. for i in 0..<min(pathBytes.count, 108) {
  69. rawPtr[i] = pathBytes[i]
  70. }
  71. }
  72. let addrSize = MemoryLayout<sockaddr_un>.size
  73. let bindResult = withUnsafePointer(to: &addr) { addrPtr in
  74. addrPtr.withMemoryRebound(to: sockaddr.self, capacity: 1) { saPtr in
  75. bind(serverFd, saPtr, socklen_t(addrSize))
  76. }
  77. }
  78. if bindResult == 0 {
  79. listen(serverFd, 10)
  80. chmod(socketPath, 0o666) // Allow clients to connect
  81. // Accept clients loop
  82. inputQueue.async {
  83. while true {
  84. let clientFd = accept(serverFd, nil, nil)
  85. if clientFd >= 0 {
  86. lock.lock()
  87. clients.append(clientFd)
  88. lock.unlock()
  89. print("arkrt: Input client connected (\(clientFd))")
  90. }
  91. }
  92. }
  93. }
  94. }
  95. // Scan and open input devices (/dev/input/event0 to event10) periodically
  96. inputQueue.async {
  97. var openedDevices = Set<String>()
  98. while true {
  99. for i in 0...10 {
  100. let path = "/dev/input/event\(i)"
  101. if !openedDevices.contains(path) {
  102. let fd = open(path, O_RDONLY)
  103. if fd >= 0 {
  104. openedDevices.insert(path)
  105. print("arkrt: Opened input device \(path)")
  106. inputQueue.async {
  107. readInputLoop(fd: fd)
  108. }
  109. }
  110. }
  111. }
  112. usleep(1_000_000) // Scan every 1 second
  113. }
  114. }
  115. }
  116. private static func readInputLoop(fd: Int32) {
  117. let evSize = MemoryLayout<input_event>.size
  118. let buf = UnsafeMutablePointer<input_event>.allocate(capacity: 1)
  119. defer { buf.deallocate(); close(fd) }
  120. while true {
  121. let bytesRead = read(fd, buf, evSize)
  122. if bytesRead == evSize {
  123. let ev = buf.pointee
  124. if ev.type == EV_KEY || ev.type == EV_REL || ev.type == EV_ABS {
  125. broadcast(type: UInt8(ev.type), code: ev.code, value: ev.value)
  126. }
  127. } else if bytesRead <= 0 {
  128. break
  129. }
  130. }
  131. }
  132. private static func broadcast(type: UInt8, code: UInt16, value: Int32) {
  133. var event = ArkInputEvent(type: type, padding: 0, code: code, value: value)
  134. lock.lock()
  135. var disconnected: [Int32] = []
  136. for clientFd in clients {
  137. var sent = 0
  138. withUnsafeBytes(of: &event) { bytes in
  139. sent = write(clientFd, bytes.baseAddress, bytes.count)
  140. }
  141. if sent < 0 {
  142. disconnected.append(clientFd)
  143. }
  144. }
  145. for d in disconnected {
  146. clients.removeAll(where: { $0 == d })
  147. close(d)
  148. }
  149. lock.unlock()
  150. }
  151. }