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.