home *** CD-ROM | disk | FTP | other *** search
- /*
- * 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
-