Last active
May 10, 2024 19:45
-
-
Save hym3242/4605bf8dc30edbe6da9c29289eac36d9 to your computer and use it in GitHub Desktop.
Dump full text content (incl. scrollback) of a window of Terminal.app with partial name of the window title.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// I have set my terminal to set wintitle with escape sequence every time a new window is created so that I can dump window by just tty name. | |
// I have this command in .profile: wintitle $(basename $(tty)) | |
// where wintitle is: | |
// $ type wintitle | |
// wintitle is a function | |
// wintitle () | |
// { | |
// echo -en "\e]0;$1\x01\e\\" | |
// } | |
// escape sequence source: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html. For more interesting escape sequence magics, checkout vttest(1) (also by Thomas E. Dickey) | |
// mostly written by chatgpt 4. No right reserved. Plz star! | |
#import <Cocoa/Cocoa.h> | |
#import <ApplicationServices/ApplicationServices.h> | |
void dumpFirstAXText(AXUIElementRef element, BOOL *found) { | |
if (*found) return; // Stop if already found a text area | |
CFTypeRef elementRole; | |
AXUIElementCopyAttributeValue(element, kAXRoleAttribute, &elementRole); | |
if (elementRole) { | |
NSString *role = CFBridgingRelease(elementRole); | |
if ([role isEqualToString:(NSString *)kAXTextAreaRole]) { | |
CFTypeRef value; | |
AXError error = AXUIElementCopyAttributeValue(element, kAXValueAttribute, &value); | |
if (error == kAXErrorSuccess && value) { | |
NSString *text = CFBridgingRelease(value); | |
printf("%s\n", [text UTF8String]); // Print to stdout | |
*found = YES; // Mark as found | |
return; // Stop after finding the first match | |
} | |
} | |
} | |
// Recursively search child elements if not found | |
CFArrayRef children; | |
AXError error = AXUIElementCopyAttributeValue(element, kAXChildrenAttribute, (CFTypeRef *)&children); | |
if (error == kAXErrorSuccess && children) { | |
for (CFIndex i = 0; i < CFArrayGetCount(children) && !*found; i++) { | |
AXUIElementRef child = (AXUIElementRef)CFArrayGetValueAtIndex(children, i); | |
dumpFirstAXText(child, found); | |
} | |
CFRelease(children); | |
} | |
} | |
int main(int argc, const char * argv[]) { | |
@autoreleasepool { | |
if (argc != 2) { | |
fprintf(stderr, "Usage: %s <window title substring>\n", argv[0]); | |
return 1; | |
} | |
// Copy argv[1] before overwriting | |
NSString *searchString = [NSString stringWithUTF8String:argv[1]]; | |
// Overwrite argv[1] in-place, to avoid dumping itself. (arguments appear in window title) | |
char* p = *(argv+1); | |
while(*p != '\0'){ | |
*p = 'x'; | |
p++; | |
} | |
NSArray *runningApps = [[NSWorkspace sharedWorkspace] runningApplications]; | |
for (NSRunningApplication *app in runningApps) { | |
if ([[app bundleIdentifier] isEqualToString:@"com.apple.Terminal"]) { | |
AXUIElementRef appElement = AXUIElementCreateApplication([app processIdentifier]); | |
CFArrayRef windows; | |
AXError error = AXUIElementCopyAttributeValue(appElement, kAXWindowsAttribute, (CFTypeRef *)&windows); | |
if (error == kAXErrorSuccess && windows) { | |
for (CFIndex i = 0; i < CFArrayGetCount(windows); i++) { | |
AXUIElementRef window = (AXUIElementRef)CFArrayGetValueAtIndex(windows, i); | |
CFTypeRef title; | |
AXError error = AXUIElementCopyAttributeValue(window, kAXTitleAttribute, &title); | |
if (error == kAXErrorSuccess && title && CFStringFind(title, (__bridge CFStringRef)(searchString), 0).location != kCFNotFound) { | |
fprintf(stderr, "Found window: %s\n", [(__bridge NSString *)(title) UTF8String]); | |
BOOL found = NO; | |
dumpFirstAXText(window, &found); // Dump content of the first AXTextArea | |
if (found) break; // Exit if found | |
} | |
if (title) CFRelease(title); | |
} | |
CFRelease(windows); | |
} | |
CFRelease(appElement); | |
break; // Stop after processing Terminal | |
} | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
compile with:
cc dumpTerminalWindowByPartialName.m -o dumpTerminalWindowByPartialName -framework Cocoa
(you will need Xcode commandline tools)then run with:
./dumpTerminalWindowByPartialName YOURWINDOWNAMEHERE