/* ** Modified version of GUSISIOUX.cp especially for Python. ** Changes (by Jack): ** - Optionally delay the console window until something is written to it. ** - Tell the upper layers whether the last command was a read or a write. ** - Tell SIOUX not to use WaitNextEvent (both Python and SIOUX trying to be ** nice to background apps means we're yielding almost 100% of the time). ** - Make sure signals are processed when returning from read/write. */ #define GUSI_SOURCE #include "GUSIInternal.h" #include "GUSISIOUX.h" #include "GUSIDevice.h" #include "GUSIDescriptor.h" #include "GUSIBasics.h" #include "GUSIDiag.h" //#ifndef WITHOUT_JACK_MODS //#include "GUSIConfig.h" //#endif #include #include #include #include #include #include #include "Python.h" #include "macglue.h" extern Boolean SIOUXUseWaitNextEvent; static PyReadHandler sInConsole = 0L; static PyWriteHandler sOutConsole = 0L; static PyWriteHandler sErrConsole = 0L; inline bool hasCustomConsole(void) { return sInConsole != 0L; } class GUSISIOUXSocket : public GUSISocket { public: ~GUSISIOUXSocket(); ssize_t read(const GUSIScatterer & buffer); ssize_t write(const GUSIGatherer & buffer); virtual int ioctl(unsigned int request, va_list arg); virtual int fstat(struct stat * buf); virtual int isatty(); bool select(bool * canRead, bool * canWrite, bool *); static GUSISIOUXSocket * Instance(int fd); private: GUSISIOUXSocket(int fd); static bool initialized; static void Initialize(); int fFd; }; class GUSISIOUXDevice : public GUSIDevice { public: static GUSISIOUXDevice * Instance(); virtual bool Want(GUSIFileToken & file); virtual GUSISocket * open(GUSIFileToken &, int flags); private: GUSISIOUXDevice() {} static GUSISIOUXDevice * sInstance; }; GUSISIOUXSocket * GUSISIOUXSocket::Instance(int fd) { return new GUSISIOUXSocket(fd); } // This declaration lies about the return type extern "C" void SIOUXHandleOneEvent(EventRecord *userevent); bool GUSISIOUXSocket::initialized = false; GUSISIOUXSocket::GUSISIOUXSocket(int fd) : fFd(fd) { if (!hasCustomConsole()) { if (!PyMac_GetDelayConsoleFlag() && !initialized) Initialize(); /* Tell the upper layers there's no unseen output */ PyMac_OutputSeen(); } } void GUSISIOUXSocket::Initialize() { if(!initialized && !hasCustomConsole()) { initialized = true; InstallConsole(0); GUSISetHook(GUSI_EventHook+nullEvent, (GUSIHook)SIOUXHandleOneEvent); GUSISetHook(GUSI_EventHook+mouseDown, (GUSIHook)SIOUXHandleOneEvent); GUSISetHook(GUSI_EventHook+mouseUp, (GUSIHook)SIOUXHandleOneEvent); GUSISetHook(GUSI_EventHook+updateEvt, (GUSIHook)SIOUXHandleOneEvent); GUSISetHook(GUSI_EventHook+diskEvt, (GUSIHook)SIOUXHandleOneEvent); GUSISetHook(GUSI_EventHook+activateEvt, (GUSIHook)SIOUXHandleOneEvent); GUSISetHook(GUSI_EventHook+osEvt, (GUSIHook)SIOUXHandleOneEvent); PyMac_InitMenuBar(); } } GUSISIOUXSocket::~GUSISIOUXSocket() { if ( !initialized || hasCustomConsole() ) return; initialized = false; RemoveConsole(); } ssize_t GUSISIOUXSocket::read(const GUSIScatterer & buffer) { if(hasCustomConsole()) { if(fFd == 0) return buffer.SetLength( sInConsole((char *) buffer.Buffer(), (int)buffer.Length())); return 0; } if ( !initialized ) Initialize(); GUSIStdioFlush(); PyMac_OutputSeen(); PyMac_RaiseConsoleWindow(); return buffer.SetLength( ReadCharsFromConsole((char *) buffer.Buffer(), (int)buffer.Length())); GUSIContext::Yield(kGUSIPoll); } ssize_t GUSISIOUXSocket::write(const GUSIGatherer & buffer) { if(hasCustomConsole()) { if(fFd == 1) return sOutConsole((char *) buffer.Buffer(), (int)buffer.Length()); else if(fFd == 2) return sErrConsole((char *) buffer.Buffer(), (int)buffer.Length()); return 0; } ssize_t rv; if ( !initialized ) Initialize(); PyMac_OutputNotSeen(); SIOUXUseWaitNextEvent = false; rv = WriteCharsToConsole((char *) buffer.Buffer(), (int)buffer.Length()); GUSIContext::Yield(kGUSIPoll); return rv; } int GUSISIOUXSocket::ioctl(unsigned int request, va_list) { switch (request) { case FIOINTERACTIVE: return 0; default: return GUSISetPosixError(EOPNOTSUPP); } } int GUSISIOUXSocket::fstat(struct stat * buf) { GUSISocket::fstat(buf); buf->st_mode = S_IFCHR | 0666; return 0; } int GUSISIOUXSocket::isatty() { return 1; } static bool input_pending() { return false; } bool GUSISIOUXSocket::select(bool * canRead, bool * canWrite, bool *) { if ( !initialized ) Initialize(); bool cond = false; if (canRead) if (*canRead = input_pending()) cond = true; if (canWrite) cond = *canWrite = true; return cond; } GUSISIOUXDevice * GUSISIOUXDevice::sInstance; GUSISIOUXDevice * GUSISIOUXDevice::Instance() { if (!sInstance) sInstance = new GUSISIOUXDevice(); return sInstance; } bool GUSISIOUXDevice::Want(GUSIFileToken & file) { switch (file.WhichRequest()) { case GUSIFileToken::kWillOpen: return file.IsDevice() && (file.StrStdStream(file.Path()) > -1); default: return false; } } GUSISocket * GUSISIOUXDevice::open(GUSIFileToken &, int) { return GUSISIOUXSocket::Instance(1); } void GUSISetupConsoleDescriptors() { GUSIDescriptorTable * table = GUSIDescriptorTable::Instance(); table->InstallSocket(GUSISIOUXSocket::Instance(0)); table->InstallSocket(GUSISIOUXSocket::Instance(1)); table->InstallSocket(GUSISIOUXSocket::Instance(2)); } void PyMac_SetConsoleHandler(PyReadHandler stdinH, PyWriteHandler stdoutH, PyWriteHandler stderrH) { if(stdinH && stdoutH && stderrH) { sInConsole = stdinH; sOutConsole = stdoutH; sErrConsole = stderrH; } } long PyMac_DummyReadHandler(char *buffer, long n) { return 0; } long PyMac_DummyWriteHandler(char *buffer, long n) { return 0; }