Soniox Mobile to Mac - Real-time Voice Dictation & Remote Input Control
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

110 lines
4.3 KiB

import Cocoa
import ApplicationServices
func extractTextAndCursor(from elem: AXUIElement) -> (String, Int, Int)? {
var roleRef: CFTypeRef?
AXUIElementCopyAttributeValue(elem, kAXRoleAttribute as CFString, &roleRef)
let role = (roleRef as? String) ?? ""
var currentText = ""
var valueRef: CFTypeRef?
if AXUIElementCopyAttributeValue(elem, kAXValueAttribute as CFString, &valueRef) == .success,
let val = valueRef {
if let str = val as? String {
currentText = str
} else if let attrStr = val as? NSAttributedString {
currentText = attrStr.string
}
}
if currentText.isEmpty {
var countRef: CFTypeRef?
if AXUIElementCopyAttributeValue(elem, kAXNumberOfCharactersAttribute as CFString, &countRef) == .success,
let count = countRef as? Int, count > 0 {
var range = CFRange(location: 0, length: count)
if let axRange = AXValueCreate(.cfRange, &range) {
var stringRef: CFTypeRef?
if AXUIElementCopyParameterizedAttributeValue(elem, kAXStringForRangeParameterizedAttribute as CFString, axRange, &stringRef) == .success,
let str = stringRef as? String {
currentText = str
}
}
}
}
var cursor = currentText.count
var selLen = 0
var rangeRef: CFTypeRef?
if AXUIElementCopyAttributeValue(elem, kAXSelectedTextRangeAttribute as CFString, &rangeRef) == .success,
let val = rangeRef {
var cfRange = CFRange()
if AXValueGetValue(val as! AXValue, .cfRange, &cfRange) {
cursor = cfRange.location
selLen = cfRange.length
}
}
if !currentText.isEmpty || role == "AXTextField" || role == "AXTextArea" || role == "AXSearchField" || rangeRef != nil {
return (currentText, cursor, selLen)
}
// Check focused child
var focusedChildRef: CFTypeRef?
if AXUIElementCopyAttributeValue(elem, kAXFocusedUIElementAttribute as CFString, &focusedChildRef) == .success,
let child = focusedChildRef {
if let res = extractTextAndCursor(from: child as! AXUIElement) {
return res
}
}
// Check children
var childrenRef: CFTypeRef?
if AXUIElementCopyAttributeValue(elem, kAXChildrenAttribute as CFString, &childrenRef) == .success,
let children = childrenRef as? [AXUIElement] {
for child in children {
var isFocusedRef: CFTypeRef?
if AXUIElementCopyAttributeValue(child, kAXFocusedAttribute as CFString, &isFocusedRef) == .success,
let isFoc = isFocusedRef as? Bool, isFoc {
if let res = extractTextAndCursor(from: child) {
return res
}
}
}
}
return nil
}
func testFullAX() {
let sysWide = AXUIElementCreateSystemWide()
AXUIElementSetAttributeValue(sysWide, "AXEnhancedUserInterface" as CFString, kCFBooleanTrue)
AXUIElementSetAttributeValue(sysWide, "AXManualAccessibility" as CFString, kCFBooleanTrue)
guard let frontApp = NSWorkspace.shared.frontmostApplication else { return }
print("Front App:", frontApp.localizedName ?? "")
let appElem = AXUIElementCreateApplication(frontApp.processIdentifier)
AXUIElementSetAttributeValue(appElem, "AXEnhancedUserInterface" as CFString, kCFBooleanTrue)
AXUIElementSetAttributeValue(appElem, "AXManualAccessibility" as CFString, kCFBooleanTrue)
var focusedElem: CFTypeRef?
if AXUIElementCopyAttributeValue(sysWide, kAXFocusedUIElementAttribute as CFString, &focusedElem) == .success,
let elem = focusedElem {
if let (text, cursor, sel) = extractTextAndCursor(from: elem as! AXUIElement) {
print("Extracted from sysWide -> Text: '\(text)', Cursor: \(cursor), Sel: \(sel)")
return
}
}
if AXUIElementCopyAttributeValue(appElem, kAXFocusedUIElementAttribute as CFString, &focusedElem) == .success,
let elem = focusedElem {
if let (text, cursor, sel) = extractTextAndCursor(from: elem as! AXUIElement) {
print("Extracted from appElem -> Text: '\(text)', Cursor: \(cursor), Sel: \(sel)")
return
}
}
print("Could not extract text")
}
testFullAX()