cfad47cfa3/src/oscurses.cc

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
/* This file implements some of the functions described in
2
 * tads2/osifc.h.  We don't need to implement them all, as most of them
3
 * are provided by tads2/osnoui.c and tads2/osgen3.c.
4
 *
5
 * This file only implements the functions that use curses routines.
6
 * Functions that don't need curses are implemented in osportable.cc.
7
 */
8
#include "common.h"
9
10
#include <stdio.h>
11
#include <stdlib.h>
12
#include <ctype.h>
13
#include <string.h>
14
//#include <assert.h>
15
#include <stddef.h>
16
17
#include "os.h"
18
extern "C" {
19
#include "osgen.h"
20
}
21
22
#include "frobtadsapp.h"
23
#include "frobcurses.h"
24
25
26
/* We define this because osgen3 needs it.
27
 */
28
int status_mode = 0;
29
30
/* We store the UNDO record limit here.  This allows us to change it
31
 * at application start-up rather than having to hard-code it at compile
32
 * time.
33
 *
34
 * We initialize it to -1 for a good reason; if the TADS3 base code
35
 * changes at some point and tries to create its internal UNDO-buffer(s)
36
 * before we get a chance to put the actual value here, we'll get a
37
 * segmentation fault (you cannot allocate negative sizes).  This will
38
 * be an indication that we must redesign the way we allow the user to
39
 * change the UNDO-size.
40
 */
41
int frobVmUndoMaxRecords = -1;
42
43
44
/* Read a character from the keyboard.
45
 *
46
 * This routine is not needed by the VM.  It's commented out rather than
47
 * left in as a dummy, so that we'll get a linker-error if the VM
48
 * decides to use it someday; that way we'll know that we must implement
49
 * it.  A dummy would result in a working build but non-working runtime.
50
 */
51
/*
52
#ifdef RUNTIME
53
int os_getc(void)
54
{
55
}
56
#endif
57
*/
58
59
60
/* Uses os_getc_raw() semantics, but with a timeout.
61
 *
62
 * If 'timeout' is 0 or negative, then the routine behaves exactly like
63
 * os_getc_raw().  If 'timeout' is positive, then we only wait for a key
64
 * for 'timeout' milliseconds.  If the operation times out before a key
65
 * has been pressed, we return 0 and set 'timedOut' to true.  If a key
66
 * is pressed before the timeout is reached, we return the same as
67
 * os_getc_raw() and set 'timedOut' to false.
68
 *
69
 * 'showCursor' enables/disables the cursor while waiting for input.
70
 */
71
static int
72
timedGetcRaw( bool showCursor, int timeout = -1, bool* timedOut = 0)
73
{
74
	// If `done' is false, it means that we have been previously
75
	// called and returned 0, so this time we should return the
76
	// extended key-code we stored last time in `extKey'.
77
	static bool done = true;
78
	static int extKey;
79
80
	if (done) {
81
		int c;
82
83
		// Read a character.
84
		c = globalApp->getRawChar(showCursor, timeout);
85
		extKey = 0;
86
87
		if (c == ERR) {
88
			if (timeout > 0) {
89
				// The operation timed out.
90
				if (timedOut != 0) *timedOut = true;
91
				return 0;
92
			}
93
			// Paranoia (ERR is only returned to indicate
94
			// that the operation timed out).
95
			// Something else happened.  Prepare to return
96
			// an EOF on our next call.
97
			extKey = CMD_EOF;
98
		}
99
100
		// If a timeout was specified and the caller wants to
101
		// know, report that no timeout occured.
102
		if (timeout > 0 and timedOut != 0) *timedOut = false;
103
		
104
		switch (c) {
105
		  // Paranoia.
106
		  // ERR should always be 0, and therefore
107
		  // already handled.  Anyway, we explicitly
108
		  // check for 0 here just in case ERR != 0.
109
		  case 0:         extKey = CMD_EOF; break;
110
		  // A Tab is not an extended character, but Tads requires
111
		  // that it is handled as one.
112
		  case '\t':      extKey = CMD_TAB; break;
113
		  case '\n':
114
		  case '\r':
115
		  case KEY_ENTER: return 13;
116
		  case KEY_DOWN:  extKey = CMD_DOWN; break;
117
		  case KEY_UP:    extKey = CMD_UP; break;
118
		  case KEY_LEFT:  extKey = CMD_LEFT; break;
119
		  case KEY_RIGHT: extKey = CMD_RIGHT; break;
120
		  case KEY_HOME:  extKey = CMD_HOME; break;
121
		  // We don't return '\b' because of paranoia;
122
		  // some systems might not use ASCII code 8 for
123
		  // '\b'.
124
		  case '\b':
125
		  case KEY_BACKSPACE: return 8;
126
		  case KEY_F(1):  extKey = CMD_F1; break;
127
		  case KEY_F(2):  extKey = CMD_F2; break;
128
		  case KEY_F(3):  extKey = CMD_F3; break;
129
		  case KEY_F(4):  extKey = CMD_F4; break;
130
		  case KEY_F(5):  extKey = CMD_F5; break;
131
		  case KEY_F(6):  extKey = CMD_F6; break;
132
		  case KEY_F(7):  extKey = CMD_F7; break;
133
		  case KEY_F(8):  extKey = CMD_F8; break;
134
		  case KEY_F(9):  extKey = CMD_F9; break;
135
		  case KEY_F(10): extKey = CMD_F10; break;
136
		  case KEY_DL:    extKey = CMD_KILL; break;
137
		  case KEY_DC:    extKey = CMD_DEL; break;
138
		  case KEY_EOL:   extKey = CMD_DEOL; break;
139
		  case KEY_NPAGE: extKey = CMD_PGDN; break;
140
		  case KEY_PPAGE: extKey = CMD_PGUP; break;
141
		  case KEY_END:   extKey = CMD_END; break;
142
		  default:
143
			// TODO: This assumes that the system only returns
144
			// unsigned characters for "normal" inputs.
145
			if (c < 0 or c > 255) {
146
				// Who knows?  Report a space so that
147
				// there's at least some feedback.
148
		  		return ' ';
149
			}
150
		}
151
152
		if (extKey == 0) {
153
			// It's a normal ASCII code (this includes Escape,
154
			// which has code 27).
155
			return c;
156
		}
157
158
		// Prepare to return the extended key-code on
159
		// our next call.
160
		done = false;
161
		return 0;
162
	}
163
164
	// We have a pending return from our last call.  Prepare to do a
165
	// normal read on our next call and return the pending result.
166
	done = true;
167
	return extKey;
168
}
169
170
171
/* Read a character from the keyboard and return the low-level,
172
 * untranslated key code whenever possible.
173
 */
174
int
175
os_getc_raw( void )
176
{
177
	// Just read a character without a timeout and return it.
178
	return timedGetcRaw(true);
179
}
180
181
182
/* Wait for a character to become available from the keyboard.
183
 *
184
 * We only implement this if we are building the interpreter.
185
 */
186
#ifdef RUNTIME
187
void
188
os_waitc( void )
189
{
190
	// Just read a character with no timeout and ignore its value.
191
	// Don't show a cursor.
192
	globalApp->getRawChar(false, 0);
193
}
194
#endif
195
196
197
/* Get an input event.
198
 */
199
int
200
os_get_event( unsigned long timeout, int use_timeout, os_event_info_t* info )
201
{
202
	int res;
203
	bool timedOut = false;
204
205
	res = timedGetcRaw(true, use_timeout ? timeout : 0, &timedOut);
206
207
	// If the timeout expired, tell TADS about it.
208
	if (use_timeout and timedOut) return OS_EVT_TIMEOUT;
209
210
	if (res == 0) {
211
		// It was an extended character; call again to get the
212
		// extended code.
213
		info->key[0] = 0;
214
		info->key[1] = timedGetcRaw(true);
215
	} else {
216
		// A normal character.  Return it as is.
217
		info->key[0] = res;
218
		info->key[1] = 0;
219
	}
220
	// Tell the caller it was an key-event.
221
	return OS_EVT_KEY;
222
}
223
224
225
/* Sleep for a while.
226
 */
227
void
228
os_sleep_ms( long delay_in_milliseconds )
229
{
230
	// Tell TADS to redraw the screen if needed.
231
	osssb_redraw_if_needed();
232
	globalApp->sleep(delay_in_milliseconds);
233
}
234
235
236
/* Terminate.
237
 *
238
 * The TADS 2 VM (not TADS 3) calls this routine when the user types
239
 * $$ABEND in a game.  The intention is to provide an emergency-exit in
240
 * case the game has fucked up somehow.  We don't do anything here, as
241
 * TADS 2 falls-back to a more sane exit-sequence when this function
242
 * actually returns.
243
 */
244
void
245
os_term( int )
246
{
247
}
248
249
250
/* Pause prior to exit, if desired.
251
 */
252
void
253
os_expause( void )
254
{
255
	if (globalApp->options.exitPause) {
256
		// Exit-pause is enabled.  Tell the user, flush any
257
		// pending output and wait for a key.
258
		os_printz("[Hit any key to exit.]");
259
		os_flush();
260
		os_waitc();
261
	}
262
}
263
264
265
/* Check for user break.
266
 *
267
 * TODO: Find out if we need this.
268
 */
269
int
270
os_break( void )
271
{
272
	return 0;
273
}
274
275
276
/* Initialize the time zone.
277
 *
278
 * TODO: Find out if this can be empty on all Unices.
279
 */
280
//void
281
//os_tzset( void )
282
//{
283
//}