summaryrefslogtreecommitdiffstats
path: root/Mac/Python/pyGUSISIOUX.cp
blob: 40670d8227880023067d540aa7fac039a49f655a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
/*
** 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 <LowMem.h>

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <console.h>

#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 (!PyMac_GetDelayConsoleFlag() && !hasCustomConsole() && !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()
{
#if !TARGET_API_MAC_CARBON
	// Jack thinks that completely removing this code is a bit
	// too much...
	QHdrPtr eventQueue = LMGetEventQueue();
	EvQElPtr element = (EvQElPtr)eventQueue->qHead;
	
	// now, count the number of pending keyDown events.
	while (element != nil) {
		if (element->evtQWhat == keyDown || element->evtQWhat == autoKey)
			return true;
		element = (EvQElPtr)element->qLink;
	}
#endif
	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;
}