This is Puppeteer.m in view mode; [Download] [Up]
/* * Puppeteer 1.1 * * Copyright (c) 1994 Primitive Software Ltd. All rights reserved. * * Author: Dave Griffiths <dave@prim.demon.co.uk> */ #import "Puppeteer.h" #import "WindowInfo.h" #import "CyberPost.h" #import <assert.h> @implementation Puppeteer + connectToApp:(const char *)theName launch:(BOOL)launch /* * Return an instance of puppeteer connected to the given app, or nil on failure. */ { id puppet = [[self alloc] init]; if ([puppet connect:theName launch:launch]) return puppet; else { [puppet free]; return nil; } } - (BOOL)connect:(const char *)theName launch:(BOOL)launch /* * Connect to the specified app. Returns YES on success. */ { /* * Because the WindowInfo object sends postscript commands, we may need * a postscript context for it to run in. */ if (!DPSGetCurrentContext()) { ctxt = DPSCreateContext(0, 0, NULL, NULL); DPSSetContext(ctxt); } appName = NXCopyStringBuffer(theName); appSpeaker = [[Speaker alloc] init]; journalSpeaker = [[Speaker alloc] init]; if (launch) { if ((appPort = NXPortFromName(appName, NULL)) == PORT_NULL) return NO; } else { if ((appPort = NXPortNameLookup(appName, NULL)) == PORT_NULL) return NO; } [appSpeaker setSendPort:appPort]; return YES; } - free { if (appName) free(appName); if (appSpeaker) [appSpeaker free]; if (journalSpeaker) [journalSpeaker free]; if (ctxt) DPSDestroyContext(ctxt); return [super free]; } - postEvent:(NXEvent *)event /* * This is the method which actually posts the events to the puppet. */ { assert(enabled); [journalSpeaker selectorRPC: "_playJournalEventType:x:y:time:flags:window:subtype:miscL0:miscL1:ctxt:" paramTypes:"iddiiiiiii", event->type, event->location.x, event->location.y, event->time, event->flags | 0x80000000, event->window, event->data.compound.subtype, event->data.compound.misc.L[0], event->data.compound.misc.L[1], event->ctxt]; return self; } - postKeyboardString:(const char *)keyString flags:(int)flags { int i, length = strlen(keyString); for (i=0; i<length; i++) { [self postKeyboardEvent:NX_KEYDOWN window:NX_KEYWINDOW flags:flags charCode:keyString[i]]; [self postKeyboardEvent:NX_KEYUP window:NX_KEYWINDOW flags:flags charCode:keyString[i]]; } return self; } - postKeyboardEvent:(int)eventType window:(int)window flags:(int)flags charCode:(char)charCode { NXEvent event; bzero(&event, sizeof(event)); event.type = eventType; event.flags = flags; event.window = window; event.data.key.charCode = charCode; [self postEvent:&event]; return self; } - postKeyCode:(char)charCode window:(int)window flags:(int)flags { [self postKeyboardEvent:NX_KEYDOWN window:window flags:flags charCode:charCode]; [self postKeyboardEvent:NX_KEYUP window:window flags:flags charCode:charCode]; return self; } - postMouseEvent:(int)eventType window:(int)window flags:(int)flags x:(double)x y:(double)y click:(int)click { NXEvent event; bzero(&event, sizeof(event)); event.type = eventType; event.flags = flags; event.window = window; event.location.x = x; event.location.y = y; event.data.mouse.click = click; [self postEvent:&event]; return self; } - postSingleClick:(int)window flags:(int)flags x:(double)x y:(double)y { [self postMouseEvent:NX_MOUSEDOWN window:window flags:flags x:x y:y click:1]; [self postMouseEvent:NX_MOUSEUP window:window flags:flags x:x y:y click:1]; return self; } - postDoubleClick:(int)window flags:(int)flags x:(double)x y:(double)y { [self postSingleClick:window flags:flags x:x y:y]; [self postMouseEvent:NX_MOUSEDOWN window:window flags:flags x:x y:y click:2]; [self postMouseEvent:NX_MOUSEUP window:window flags:flags x:x y:y click:2]; return self; } - postTripleClick:(int)window flags:(int)flags x:(double)x y:(double)y { [self postDoubleClick:window flags:flags x:x y:y]; [self postMouseEvent:NX_MOUSEDOWN window:window flags:flags x:x y:y click:3]; [self postMouseEvent:NX_MOUSEUP window:window flags:flags x:x y:y click:3]; return self; } - postActivate:(BOOL)activate { NXEvent event; bzero(&event, sizeof(event)); event.type = NX_KITDEFINED; event.data.compound.subtype = activate ? NX_APPACT : NX_APPDEACT; [self postEvent:&event]; return self; } - dragWindow:(int)winNumber deltaX:(double)x deltaY:(double)y { NXEvent event; bzero(&event, sizeof(event)); event.type = NX_JOURNALEVENT; event.data.compound.subtype = NX_WINDRAGGED; event.window = winNumber; event.location.x = x; event.location.y = y; event.data.mouse.click = 1; [self postEvent:&event]; return self; } - attachStrings { port_t retPort; if (enabled) return self; [appSpeaker selectorRPC:"_initJournaling:::" paramTypes:"isS", 0, 0, &retPort]; [journalSpeaker setSendPort:retPort]; [journalSpeaker selectorRPC:"_setStatus:" paramTypes:"i", 1]; enabled = YES; return self; } - releaseStrings { if (!enabled) return self; [journalSpeaker selectorRPC:"_setStatus:" paramTypes:"i", 0]; enabled = NO; return self; } - (int)getPid /* * Returns the pid of the application, or -1 if it can't be found. */ { char psLine[500]; FILE *psPipe; int length = strlen(appName); if (pid) return pid; if ((psPipe = popen("ps -axc", "r")) == NULL) { NXRunAlertPanel(NULL, "Could not open pipe to \"ps\"", NULL, NULL, NULL); return -1; } fgets(psLine, 500, psPipe); // Skip first line while (fgets(psLine, 500, psPipe) != NULL) { if (!strncmp(&psLine[20], appName, length)) { sscanf(psLine, "%d", &pid); break; } } pclose(psPipe); if (pid) return pid; else return -1; } - (int)getContext /* * Returns the application's postscript context. */ { int numWins, *winList, i, windowPid; if (context) return context; [self getPid]; if (pid < 0) return 0; PScountwindowlist(0, &numWins); winList = (int *)calloc(sizeof(int), numWins); PSwindowlist(0, numWins, winList); for (i=0; i<numWins; i++) { myCurrentOwner(winList[i], &context); myPid(context, &windowPid); if (windowPid == pid) break; } if (i == numWins) { fprintf(stderr, "Puppeteer: failed to find application's context\n"); context = 0; } free(winList); return context; } - windowList /* * Returns a list of this application's windows. A new list is created each time this * method is called, and it is the caller's responsibility to free it and it's contents. */ { int numWins, *winList, i; unsigned int winNumber; id window, windowList; [self getContext]; if (!context) return NULL; PScountwindowlist(context, &numWins); winList = (int *)calloc(sizeof(int), numWins); PSwindowlist(context, numWins, winList); windowList = [[List alloc] init]; for (i=0; i<numWins; i++) { NX_DURING NXConvertGlobalToWinNum(winList[i], &winNumber); NX_HANDLER switch (NXLocalHandler.code) { case dps_err_ps: winNumber = 0; break; default: NX_RERAISE(); } NX_ENDHANDLER window = [[WindowInfo alloc] initLocalNumber:winNumber globalNumber:winList[i]]; [windowList addObject:window]; } free(winList); return windowList; } - (int)windowCount /* * Return the number of windows belonging to puppet. */ { int windowCount; [self getContext]; if (!context) return 0; PScountwindowlist(context, &windowCount); return windowCount; } #define DELTA_X 1 - windowForPseudoNumber:(int)pseudoNumber /* * Returns a WindowInfo object for the given pseudo window number (eg NX_KEYWINDOW), * or nil if it can't be determined. */ { id iList, fList, iWin, fWin; int i, f, iCount, fCount, retry, iNum, fNum; NXRect *iFrame, fFrame; assert(pseudoNumber < 0); /* * First save the initial positions of all the windows. */ iList = [self windowList]; iCount = [iList count]; iFrame = (NXRect *)malloc(iCount*sizeof(NXRect)); for (i=0; i<iCount; i++) { iWin = [iList objectAt:i]; [iWin getFrame:&iFrame[i]]; } /* * Now move the window by one pixel. */ [self dragWindow:pseudoNumber deltaX:DELTA_X deltaY:0]; for (retry=0; retry<10; retry++) { fList = [self windowList]; for (i=0; i<iCount; i++) { iWin = [iList objectAt:i]; iNum = [iWin localWindowNumber]; fCount = [fList count]; for (f=0; f<fCount; f++) { fWin = [fList objectAt:f]; fNum = [fWin localWindowNumber]; if (fNum == iNum) break; } if (f<fCount) { [fWin getFrame:&fFrame]; if (iFrame[i].origin.x == (fFrame.origin.x - DELTA_X)) { [[iList freeObjects] free]; //[[fList freeObjects] free]; free(iFrame); return fWin; } } } [[fList freeObjects] free]; sleep(1); printf("Failed...\n"); } [[iList freeObjects] free]; free(iFrame); printf("Failed utterly!\n"); return NULL; } - keyWindow { return [self windowForPseudoNumber:NX_KEYWINDOW]; } - mainWindow { return [self windowForPseudoNumber:NX_MAINWINDOW]; } - mainMenu { return [self windowForPseudoNumber:NX_MAINMENU]; } - ping /* * This method simply sends a message to the puppet application and waits for it to * return. This ensures that the application has processed all outstanding events. * We use the standard msgVersion:ok: method and ignore the return value. */ { int ret, ok = 0; char *const *version = 0; ret = [journalSpeaker msgVersion:version ok:&ok]; return self; } - appSpeaker { return appSpeaker; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.