~ubuntu-branches/ubuntu/wily/gargoyle-free/wily-proposed

« back to all changes in this revision

Viewing changes to tads/tads2/osgen3.c

  • Committer: Bazaar Package Importer
  • Author(s): Sylvain Beucler
  • Date: 2009-09-11 20:09:43 UTC
  • Revision ID: james.westby@ubuntu.com-20090911200943-idgzoyupq6650zpn
Tags: upstream-2009-08-25
ImportĀ upstreamĀ versionĀ 2009-08-25

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#ifdef RCSID
 
2
static char RCSid[] =
 
3
"$Header: d:/cvsroot/tads/TADS2/OSGEN.C,v 1.3 1999/07/11 00:46:30 MJRoberts Exp $";
 
4
#endif
 
5
 
 
6
/* 
 
7
 *   Copyright (c) 1990, 2002 Michael J. Roberts.  All Rights Reserved.
 
8
 *   
 
9
 *   Please see the accompanying license file, LICENSE.TXT, for information
 
10
 *   on using and copying this software.  
 
11
 */
 
12
/*
 
13
Name
 
14
  osgen3  - Operating System dependent functions, general implementation
 
15
            TADS 3 Version, with new "banner" interface
 
16
Function
 
17
  This module contains certain OS-dependent functions that are common
 
18
  to many character-mode platforms.  ("Character mode" means that the
 
19
  display is organized as a rectangular grid of characters in a monospaced
 
20
  font.  This module isn't usable for most GUI systems, because it doesn't
 
21
  have any support for variable-pitch fonts - GUI ports generally need to
 
22
  provide their own custom versions of the os_xxx() functions this module
 
23
  module provides.  On GUI systems you should simply omit this entire
 
24
  module from the build, and instead substitute a new module of your own
 
25
  creation that defines your custom versions of the necessary os_xxx()
 
26
  functions.)
 
27
 
 
28
  Some routines in this file are selectively enabled according to macros
 
29
  defined in os.h, since some ports that use this file will want to provide
 
30
  their own custom versions of these routines instead of the ones defined
 
31
  here.  The macros and associated functions are:
 
32
 
 
33
    USE_STDIO     - implement os_print, os_flush, os_gets with stdio functions
 
34
    USE_DOSEXT    - implement os_remext, os_defext using MSDOS-like filename
 
35
                    conventions
 
36
    USE_NULLINIT  - implement os_init and os_term as do-nothing routines
 
37
    USE_NULLPAUSE - implement os_expause as a do-nothing routine
 
38
    USE_EXPAUSE   - use an os_expause that prints a 'strike any key' message
 
39
                    and calls os_waitc
 
40
    USE_TIMERAND  - implement os_rand using localtime() as a seed
 
41
    USE_NULLSTAT  - use a do-nothing os_status function
 
42
    USE_NULLSCORE - use a do-nothing os_score function
 
43
    RUNTIME       - enable character-mode console implementation  
 
44
    USE_STATLINE  - implement os_status and os_score using character-mode
 
45
                    status line implementation
 
46
    USE_OVWCHK    - implements default saved file overwrite check
 
47
    USE_NULLSTYPE - use a dummy os_settype routine
 
48
    USE_NULL_SET_TITLE - use an empty os_set_title() implementation
 
49
 
 
50
    If USE_STDIO is defined, we'll implicitly define USE_STDIO_INPDLG.
 
51
 
 
52
    If USE_STATLINE is defined, certain subroutines must be provided for
 
53
    your platform that handle the character-mode console:
 
54
        ossclr - clears a portion of the screen
 
55
        ossdsp - displays text in a given color at a given location
 
56
        ossscr - scroll down (i.e., moves a block of screen up)
 
57
        ossscu - scroll up (i.e., moves a block of screen down)
 
58
        ossloc - locate cursor
 
59
 
 
60
    If USE_STATLINE is defined, certain sub-options can be enabled:
 
61
        USE_SCROLLBACK - include output buffer capture in console system
 
62
        USE_HISTORY    - include command editing and history in console system
 
63
Notes
 
64
 
 
65
Modified
 
66
  01/01/98 MJRoberts     - moved certain osgen.c routines to osnoui.c  
 
67
  04/24/93 JEras         - add os_locate() for locating tads-related files
 
68
  04/12/92 MJRoberts     - add os_strsc (string score) function
 
69
  03/26/92 MJRoberts     - add os_setcolor function
 
70
  09/26/91 MJRoberts     - os/2 user exit support
 
71
  09/04/91 MJRoberts     - stop reading resources if we find '$eof' resource
 
72
  08/28/91 MJRoberts     - debugger bug fix
 
73
  08/01/91 MJRoberts     - make runstat work correctly
 
74
  07/30/91 MJRoberts     - add debug active/inactive visual cue
 
75
  05/23/91 MJRoberts     - add user exit reader
 
76
  04/08/91 MJRoberts     - add full-screen debugging support
 
77
  03/10/91 MJRoberts     - integrate John's qa-scripter mods
 
78
  11/27/90 MJRoberts     - use time() not localtime() in os_rand; cast time_t
 
79
  11/15/90 MJRoberts     - created (split off from os.c)
 
80
*/
 
81
 
 
82
#define OSGEN_INIT
 
83
# include "os.h"
 
84
#undef OSGEN_INIT
 
85
 
 
86
#include "osgen.h"
 
87
 
 
88
#include <stdio.h>
 
89
#include <stdlib.h>
 
90
#include <string.h>
 
91
#include <stdarg.h>
 
92
#include <ctype.h>
 
93
#include <assert.h>
 
94
 
 
95
#include "run.h"
 
96
 
 
97
#if defined(TURBO) || defined(DJGPP)
 
98
#include "io.h"
 
99
#endif    
 
100
 
 
101
#include "lib.h"
 
102
#include "tio.h"
 
103
 
 
104
/*
 
105
 *   Flag: use "plain" mode.  If this is set, we'll use plain stdio output
 
106
 *   rather than our window-oriented display.  
 
107
 */
 
108
int os_f_plain = 0;
 
109
 
 
110
#ifdef RUNTIME
 
111
# ifdef USE_SCROLLBACK
 
112
 
 
113
/*
 
114
 *   Screen size variables.  The underlying system-specific "oss" code must
 
115
 *   initialize these during startup and must keep them up-to-date if the
 
116
 *   screen size ever changes.  
 
117
 */
 
118
int G_oss_screen_width = 80;
 
119
int G_oss_screen_height = 24;
 
120
 
 
121
 
 
122
# endif /* USE_SCROLLBACK */
 
123
#endif /* RUNTIME */
 
124
 
 
125
/*
 
126
 *   The special character codes for controlling color. 
 
127
 */
 
128
 
 
129
/* 
 
130
 *   set text attributes: this is followed by one byte giving the new
 
131
 *   attribute codes 
 
132
 */
 
133
#define OSGEN_ATTR            1
 
134
 
 
135
/* 
 
136
 *   Set text color: this is followed by two bytes giving the foreground and
 
137
 *   background colors as OSGEN_COLOR_xxx codes.
 
138
 *   
 
139
 *   Note well that the colors encoded in this escape sequence are
 
140
 *   OSGEN_COLOR_xxx values, not os_color_t values.  The latter require 32
 
141
 *   bits because they can store 24-bit RGB values plus some special
 
142
 *   parameter codes, while our internal OSGEN_COLOR_xxx values are only a
 
143
 *   byte each.  
 
144
 */
 
145
#define OSGEN_COLOR           2
 
146
 
 
147
 
 
148
/*
 
149
 *   If this port is to use the default saved file overwrite check, define
 
150
 *   USE_OVWCHK.  This routine tries to open the file; if successful, the
 
151
 *   file is closed and we ask the user if they're sure they want to overwrite
 
152
 *   the file.
 
153
 */
 
154
#ifdef USE_OVWCHK
 
155
int os_chkovw(char *filename)
 
156
{
 
157
    FILE *fp;
 
158
    
 
159
    if ((fp = fopen( filename, "r" )) != 0)
 
160
    {
 
161
        char buf[128];
 
162
        
 
163
        fclose(fp);
 
164
        os_printz("That file already exists.  Overwrite it? (y/n) >");
 
165
        os_gets((uchar *)buf, sizeof(buf));
 
166
        if (buf[0] != 'y' && buf[0] != 'Y')
 
167
            return 1;
 
168
    }
 
169
    return 0;
 
170
}
 
171
#endif /* USE_OVWCHK */
 
172
 
 
173
/* 
 
174
 *   non-stop mode does nothing in character-mode implementations, since the
 
175
 *   portable console layer handles MORE mode 
 
176
 */
 
177
void os_nonstop_mode(int flag)
 
178
{
 
179
}
 
180
 
 
181
/* ------------------------------------------------------------------------ */
 
182
/*
 
183
 *   Ports can implement os_flush and os_gets as calls to the stdio routines
 
184
 *   of the same name, and os_print and os_printz using the fputs() to
 
185
 *   stdout, by defining USE_STDIO.  These definitions can be used for any
 
186
 *   port for which the standard C run-time library is available.  
 
187
 */
 
188
 
 
189
#ifdef USE_STDIO
 
190
 
 
191
/* 
 
192
 *   os_printz works just like fputs() to stdout: we write a null-terminated
 
193
 *   string to the standard output.  
 
194
 */
 
195
void os_printz(const char *str)
 
196
{
 
197
    fputs(str, stdout);
 
198
}
 
199
 
 
200
/*
 
201
 *   os_puts works like fputs() to stdout, except that we are given a
 
202
 *   separate length, and the string might not be null-terminated 
 
203
 */
 
204
void os_print(const char *str, size_t len)
 
205
{
 
206
    printf("%.*s", (int)len, str);
 
207
}
 
208
 
 
209
/*
 
210
 *   os_flush forces output of anything buffered for standard output.  It
 
211
 *   is generally used prior to waiting for a key (so the normal flushing
 
212
 *   may not occur, as it does when asking for a line of input).  
 
213
 */
 
214
void os_flush(void)
 
215
{
 
216
    fflush(stdout);
 
217
}
 
218
 
 
219
/* 
 
220
 *   update the display - since we're using text mode, there's nothing we
 
221
 *   need to do 
 
222
 */
 
223
void os_update_display(void)
 
224
{
 
225
}
 
226
 
 
227
/*
 
228
 *   os_gets performs the same function as gets().  It should get a
 
229
 *   string from the keyboard, echoing it and allowing any editing
 
230
 *   appropriate to the system, and return the null-terminated string as
 
231
 *   the function's value.  The closing newline should NOT be included in
 
232
 *   the string.  
 
233
 */
 
234
uchar *os_gets(uchar *s, size_t bufl)
 
235
{
 
236
    /* make sure everything we've displayed so far is actually displayed */
 
237
    fflush(stdout);
 
238
 
 
239
    /* get a line of input from the standard input */
 
240
    return((uchar *)fgets((char *)s, bufl, stdin));
 
241
}
 
242
 
 
243
/*
 
244
 *   The default stdio implementation does not support reading a line of
 
245
 *   text with timeout.  
 
246
 */
 
247
int os_gets_timeout(unsigned char *buf, size_t bufl,
 
248
                    unsigned long timeout, int resume_editing)
 
249
{
 
250
    /* tell the caller this operation is not supported */
 
251
    return OS_EVT_NOTIMEOUT;
 
252
}
 
253
 
 
254
/* 
 
255
 *   since we don't support os_gets_timeout(), we don't need to do anything
 
256
 *   in the cancel routine
 
257
 */
 
258
void os_gets_cancel(int reset)
 
259
{
 
260
    /* os_gets_timeout doesn't do anything, so neither do we */
 
261
}
 
262
 
 
263
/*
 
264
 *   Get an event - stdio version.  This version does not accept a timeout
 
265
 *   value, and can only get a keystroke.  
 
266
 */
 
267
int os_get_event(unsigned long timeout, int use_timeout,
 
268
                 os_event_info_t *info)
 
269
{
 
270
    /* if there's a timeout, return an error indicating we don't allow it */
 
271
    if (use_timeout)
 
272
        return OS_EVT_NOTIMEOUT;
 
273
 
 
274
    /* get a key the normal way */
 
275
    info->key[0] = os_getc();
 
276
 
 
277
    /* if it's an extended key, get the other key */
 
278
    if (info->key[0] == 0)
 
279
    {
 
280
        /* get the extended key code */
 
281
        info->key[1] = os_getc();
 
282
 
 
283
        /* if it's EOF, return an EOF event rather than a key event */
 
284
        if (info->key[1] == CMD_EOF)
 
285
            return OS_EVT_EOF;
 
286
    }
 
287
 
 
288
    /* return the keyboard event */
 
289
    return OS_EVT_KEY;
 
290
}
 
291
 
 
292
#endif /* USE_STDIO */
 
293
 
 
294
/******************************************************************************
 
295
* Ports without any special initialization/termination requirements can define
 
296
* USE_NULLINIT to pick up the default definitions below.  These do nothing, so
 
297
* ports requiring special handling at startup and/or shutdown time must define
 
298
* their own versions of these routines.
 
299
******************************************************************************/
 
300
 
 
301
#ifdef USE_NULLINIT
 
302
/* os_init returns 0 for success, 1 for failure.  The arguments are &argc, the
 
303
*  address of the count of arguments to the program, and argv, the address of
 
304
*  an array of up to 10 pointers to those arguments.  For systems which don't
 
305
*  pass a standard command line (such as the Mac Finder), the arguments should
 
306
*  be read here using some alternate mechanism (an alert box, for instance),
 
307
*  and the values of argc and argv[] updated accordingly.  Note that a maximum
 
308
*  of 10 arguments are allowed to be stored in the argv[] array.  The command
 
309
*  line itself can be stored in buf, which is a buffer passed by the caller
 
310
*  guaranteed to be bufsiz bytes long.
 
311
*
 
312
*  Unix conventions are followed, so argc is 1 when no arguments are present.
 
313
*  The final argument is a prompt string which can be used to ask the user for
 
314
*  a command line; its use is not required, but may be desirable for producing
 
315
*  a relevant prompt message.  See the Mac implementation for a detailed
 
316
*  example of how this mechanism is used.
 
317
*/
 
318
int os_init(int *argc, char *argv[], const char *prompt,
 
319
            char *buf, int bufsiz)
 
320
{
 
321
    return 0;
 
322
}
 
323
 
 
324
/*
 
325
 *   uninitialize 
 
326
 */
 
327
void os_uninit(void)
 
328
{
 
329
}
 
330
 
 
331
/* 
 
332
 *   os_term should perform any necessary cleaning up, then terminate the
 
333
 *   program.  The int argument is a return code to be passed to the
 
334
 *   caller, generally 0 for success and other for failure.  
 
335
 */
 
336
void os_term(int rc)
 
337
{
 
338
    exit(rc);
 
339
}
 
340
#endif /* USE_NULLINIT */
 
341
 
 
342
/* ------------------------------------------------------------------------ */
 
343
/*   
 
344
 *   Ports can define USE_NULLPAUSE if no pause is required on exit.
 
345
 *   
 
346
 *   Ports needing an exit pause, and can simply print a message (with
 
347
 *   os_print) and wait for a key (with os_getc) can define USE_EXPAUSE.  
 
348
 */
 
349
 
 
350
#ifdef USE_NULLPAUSE
 
351
void os_expause(void)
 
352
{
 
353
    /* does nothing */
 
354
}
 
355
#endif /* USE_NULLPAUSE */
 
356
 
 
357
#ifdef USE_EXPAUSE
 
358
void os_expause(void)
 
359
{
 
360
    os_printz("(Strike any key to exit...)");
 
361
    os_flush();
 
362
    os_waitc();
 
363
}
 
364
#endif /* USE_EXPAUSE */
 
365
 
 
366
 
 
367
#ifdef USE_NULLSTAT
 
368
/*
 
369
 *   USE_NULLSTAT defines a do-nothing version of os_status.
 
370
 */
 
371
void os_status(int stat)
 
372
{
 
373
    /* ignore the new status */
 
374
}
 
375
 
 
376
int os_get_status()
 
377
{
 
378
    return 0;
 
379
}
 
380
#endif /* USE_NULLSTAT */
 
381
 
 
382
#ifdef USE_NULLSCORE
 
383
/*
 
384
 *   USE_NULLSCORE defines a do-nothing version of os_score.
 
385
 */
 
386
void os_score(int cur, int turncount)
 
387
{
 
388
    /* ignore the score information */
 
389
}
 
390
 
 
391
void os_strsc(const char *p)
 
392
{
 
393
    /* ignore */
 
394
}
 
395
#endif /* USE_NULLSCORE */
 
396
 
 
397
/* ------------------------------------------------------------------------ */
 
398
/*
 
399
 *   Scrollback 
 
400
 */
 
401
 
 
402
/* forward-declare the window control structure type */
 
403
typedef struct osgen_win_t osgen_win_t;
 
404
 
 
405
/*
 
406
 *   We can be compiled with or without scrollback.  The first version
 
407
 *   defines the scrollback implementation; the second just defines some
 
408
 *   dummy functions for the non-scrollback implementation.  
 
409
 */
 
410
# ifdef USE_SCROLLBACK
 
411
 
 
412
/* ------------------------------------------------------------------------ */
 
413
/* 
 
414
 *   Character color structure 
 
415
 */
 
416
typedef struct osgen_charcolor_t osgen_charcolor_t;
 
417
struct osgen_charcolor_t
 
418
{
 
419
    /* foreground color, as an OSGEN_COLOR_xxx value */
 
420
    char fg;
 
421
 
 
422
    /* background color, as an OSGEN_COLOR_xxx value */
 
423
    char bg;
 
424
};
 
425
 
 
426
/*
 
427
 *   Window control structure.  Each on-screen window is represented by one
 
428
 *   of these structures.  
 
429
 */
 
430
struct osgen_win_t
 
431
{
 
432
    /* type of the window - this is an OS_BANNER_TYPE_xxx code */
 
433
    int win_type;
 
434
 
 
435
    /* flags */
 
436
    unsigned int flags;
 
437
 
 
438
    /* next window in window list */
 
439
    osgen_win_t *nxt;
 
440
 
 
441
    /* 
 
442
     *   Parent window.  We take our display area out of the parent window's
 
443
     *   allocated space at the time we lay out this window.  
 
444
     */
 
445
    osgen_win_t *parent;
 
446
 
 
447
    /* head of list of children of this window */
 
448
    osgen_win_t *first_child;
 
449
 
 
450
    /* 
 
451
     *   The window's alignment type - this determines where the window goes
 
452
     *   relative to the main window.  This is an OS_BANNER_ALIGN_xxx
 
453
     *   alignment type code.  
 
454
     */
 
455
    int alignment;
 
456
 
 
457
    /* size type (as an OS_BANNER_SIZE_xxx value) */
 
458
    int size_type;
 
459
 
 
460
    /* 
 
461
     *   The window's size.  If size_type is OS_BANNER_SIZE_ABS, this is the
 
462
     *   size of the window in character cells.  If size_type is
 
463
     *   OS_BANNER_SIZE_PCT, this is given as a percentage of the full screen
 
464
     *   size.  
 
465
     */
 
466
    int size;
 
467
 
 
468
    /* upper-left corner position of window on screen */
 
469
    int winx;
 
470
    int winy;
 
471
 
 
472
    /* size of window on screen */
 
473
    size_t wid;
 
474
    size_t ht;
 
475
 
 
476
    /* 
 
477
     *   Cursor position (location of next output).  These are given in
 
478
     *   "document coordinates", which is to say that they're relative to
 
479
     *   the start of the text in the buffer.
 
480
     *   
 
481
     *   To translate to "window coordinates", simply subtract the scrolling
 
482
     *   offsets, which give the document coordinates of the first character
 
483
     *   displayed at the upper left corner of the window.
 
484
     *   
 
485
     *   To translate to absolute screen coordinates, first subtract the
 
486
     *   scrolling offsets to get window coordinates, then add the window's
 
487
     *   screen position (winx,winy).  
 
488
     */
 
489
    int x;
 
490
    int y;
 
491
 
 
492
    /* 
 
493
     *   maximum row and line where we've actually written any text (this
 
494
     *   can be used for purposes like setting horizontal scrollbar limits
 
495
     *   and sizing a window horizontally to its contents) 
 
496
     */
 
497
    int xmax;
 
498
    int ymax;
 
499
 
 
500
    /* scrolling offset of text displayed in window */
 
501
    int scrollx;
 
502
    int scrolly;
 
503
 
 
504
    /* 
 
505
     *   current text foreground and background colors (as OSGEN_COLOR_xxx
 
506
     *   values) 
 
507
     */
 
508
    char txtfg;
 
509
    char txtbg;
 
510
 
 
511
    /* current text attributes */
 
512
    int txtattr;
 
513
 
 
514
    /* window fill color (and oss color code for same) */
 
515
    char fillcolor;
 
516
    int oss_fillcolor;
 
517
};
 
518
 
 
519
/*
 
520
 *   Ordinary text stream window.  This is a subclass of the basic
 
521
 *   osgen_win_t window type, used when win_type is OS_BANNER_TYPE_TEXT.
 
522
 *   
 
523
 *   Ordinary text windows keep a circular buffer of scrollback text.  We
 
524
 *   optimize space by using escape codes embedded in the saved text stream
 
525
 *   to select colors and attributes.
 
526
 *   
 
527
 *   In the circular text buffer, each line ends with a null byte.  We keep
 
528
 *   an array of line-start pointers to make it fast and easy to find the
 
529
 *   first byte of a particular line.  The line-start array is also
 
530
 *   circular, and is organized in ascending order of row number.  
 
531
 */
 
532
typedef struct osgen_txtwin_t osgen_txtwin_t;
 
533
struct osgen_txtwin_t
 
534
{
 
535
    /* embed the base class */
 
536
    osgen_win_t base;
 
537
 
 
538
    /* text color and attributes at start of latest line */
 
539
    int solfg;
 
540
    int solbg;
 
541
    int solattr;
 
542
            
 
543
    /* window text buffer, and size of the buffer */
 
544
    char *txtbuf;
 
545
    size_t txtbufsiz;
 
546
    
 
547
    /* next free byte of window text buffer */
 
548
    char *txtfree;
 
549
    
 
550
    /* circular array of line-start pointers */
 
551
    char **line_ptr;
 
552
    size_t line_ptr_cnt;
 
553
    
 
554
    /* index of first line-start pointer in use */
 
555
    size_t first_line;
 
556
    
 
557
    /* number of lines of text stored in the buffer */
 
558
    size_t line_count;
 
559
};
 
560
 
 
561
/*
 
562
 *   Text Grid window.  This is a subclass of the basic window type,
 
563
 *   osgen_win_t, used when win_type is OS_BANNER_TYPE_TEXTGRID.
 
564
 *   
 
565
 *   A text grid window keeps a simple rectangular array of text, and a
 
566
 *   corresponding array of the color of each character.  The size of each
 
567
 *   array is at least as large as the window's actual area on the screen;
 
568
 *   when we resize the window, we'll reallocate the arrays at a larger size
 
569
 *   if the window has expanded beyond the stored size.  We don't keep any
 
570
 *   scrollback information in a text grid; we only keep enough to cover
 
571
 *   what's actually on the screen.  
 
572
 */
 
573
typedef struct osgen_gridwin_t osgen_gridwin_t;
 
574
struct osgen_gridwin_t
 
575
{
 
576
    /* embed the base class */
 
577
    osgen_win_t base;
 
578
    
 
579
    /* width and height of the text and color arrays */
 
580
    size_t grid_wid;
 
581
    size_t grid_ht;
 
582
 
 
583
    /* text array */
 
584
    char *grid_txt;
 
585
    
 
586
    /* color array */
 
587
    osgen_charcolor_t *grid_color;
 
588
};
 
589
 
 
590
/*
 
591
 *   Window flags 
 
592
 */
 
593
 
 
594
/* keep the cursor visible when adding text to the window */
 
595
#define OSGEN_AUTO_VSCROLL    0x0001
 
596
 
 
597
/* the window buffer is full and we're allowing nothing more to enter it */
 
598
#define OSGEN_FULL            0x0002
 
599
 
 
600
/* the window is in deferred-redraw mode */
 
601
#define OSGEN_DEFER_REDRAW    0x0004
 
602
 
 
603
/* 
 
604
 *   MORE mode in the banner.  Note that we keep track of this only so that
 
605
 *   we can indicate it on queries for the banner style; we count on the
 
606
 *   caller to handle the actual prompting for us.  
 
607
 */
 
608
#define OSGEN_MOREMODE        0x0008
 
609
 
 
610
/* child banner "strut" flags */
 
611
#define OSGEN_VSTRUT          0x0010
 
612
#define OSGEN_HSTRUT          0x0020
 
613
 
 
614
/* 
 
615
 *   The main text area window.  This window is special, because it's the
 
616
 *   root of the window tree.  
 
617
 */
 
618
static osfar_t osgen_txtwin_t *S_main_win = 0;
 
619
 
 
620
/* default status line window */
 
621
static osfar_t osgen_txtwin_t *S_status_win = 0;
 
622
 
 
623
/* default input/output window (for os_print, os_gets, etc) */
 
624
static osfar_t osgen_txtwin_t *S_default_win = 0;
 
625
 
 
626
/* current scrollback-mode window */
 
627
static osfar_t osgen_txtwin_t *S_sbmode_win = 0;
 
628
 
 
629
/* scrollback mode settings */
 
630
static osfar_t int S_sbmode_orig_scrolly;
 
631
static osfar_t int S_sbmode_orig_x;
 
632
static osfar_t char **S_sbmode_orig_last_line;
 
633
static osfar_t char *S_sbmode_orig_txtfree;
 
634
 
 
635
/* 
 
636
 *   flag: we're using a special cursor position; we use this to override our
 
637
 *   normal default cursor position 
 
638
 */
 
639
static osfar_t int S_special_cursor_pos = FALSE;
 
640
static osfar_t int S_special_cursor_x = 0;
 
641
static osfar_t int S_special_cursor_y = 0;
 
642
 
 
643
/* 
 
644
 *   Flag: deferred redraw required.  This indicates that something happened
 
645
 *   that requires redrawing the screen, but we didn't bother actually doing
 
646
 *   the redrawing immediately in case other things that would also require
 
647
 *   redrawing were to occur shortly. 
 
648
 */
 
649
static osfar_t int S_deferred_redraw = FALSE;
 
650
 
 
651
/*
 
652
 *   Input buffer state.  This information is defined statically because
 
653
 *   os_gets_timeout() can carry the information from invocation to
 
654
 *   invocation when input editing is interrupted by a tmieout.  
 
655
 */
 
656
static osfar_t char S_gets_internal_buf[256];       /* internal save buffer */
 
657
static osfar_t char *S_gets_buf = S_gets_internal_buf;  /* current save buf */
 
658
static osfar_t char *S_gets_buf_end = 0;             /* end of input buffer */
 
659
static osfar_t size_t S_gets_buf_siz = sizeof(S_gets_internal_buf); /* size */
 
660
static osfar_t int S_gets_ofs;       /* offset in buffer of insertion point */
 
661
static osfar_t char *S_gets_curhist;             /* current history pointer */
 
662
static osfar_t int S_gets_x, S_gets_y;             /* saved cursor position */
 
663
 
 
664
# ifdef USE_HISTORY
 
665
/* save buffer for line being edited before history recall began */
 
666
static osfar_t char S_hist_sav_internal[256]; 
 
667
static osfar_t char *S_hist_sav = S_hist_sav_internal;
 
668
static osfar_t size_t S_hist_sav_siz = sizeof(S_hist_sav_internal);
 
669
# endif /* USE_HISTORY */
 
670
 
 
671
/*
 
672
 *   Flag: input is already in progress.  When os_gets_timeout() returns
 
673
 *   with OS_EVT_TIMEOUT, it sets this flag to true.  os_gets_cancel() sets
 
674
 *   this flag to false.
 
675
 *   
 
676
 *   When os_gets_timeout() is called again, it checks this flag to see if
 
677
 *   the input session was cancelled; if not, the routine knows that the
 
678
 *   partially-edited input line is already displayed where it left off,
 
679
 *   because the display has not been modified since the interrupted call to
 
680
 *   os_gets_timeout() returned.  
 
681
 */
 
682
static osfar_t int S_gets_in_progress = FALSE;
 
683
 
 
684
/* forward declarations */
 
685
static void osssb_add_color_code(osgen_txtwin_t *win);
 
686
static void osgen_gridwin_clear(osgen_gridwin_t *win, size_t ofs, size_t len);
 
687
static void osgen_redraw_win(osgen_win_t *win);
 
688
static void osgen_scrdisp(osgen_win_t *win, int x, int y, int len);
 
689
static void osgen_gets_redraw_cmdline(void);
 
690
 
 
691
/*
 
692
 *   Delete a window 
 
693
 */
 
694
static void osgen_delete_win(osgen_win_t *win)
 
695
{
 
696
    osgen_win_t *prv;
 
697
    osgen_win_t *cur;
 
698
    osgen_txtwin_t *twin;
 
699
    osgen_gridwin_t *gwin;
 
700
 
 
701
    /* if we have a parent, remove ourselves from our parent's child list */
 
702
    if (win->parent != 0)
 
703
    {
 
704
        /* scan our parent's child list */
 
705
        for (prv = 0, cur = win->parent->first_child ;
 
706
             cur != 0 && cur != win ;
 
707
             prv = cur, cur = cur->nxt) ;
 
708
 
 
709
        /* if we found it, unlink it */
 
710
        if (cur != 0)
 
711
        {
 
712
            /* set the previous item's forward pointer */
 
713
            if (prv == 0)
 
714
                win->parent->first_child = win->nxt;
 
715
            else
 
716
                prv->nxt = win->nxt;
 
717
        }
 
718
    }
 
719
 
 
720
    /* 
 
721
     *   Remove the parent reference from each child of this window.  We're
 
722
     *   going to be deleted, so we can't keep references from our children
 
723
     *   to us.  
 
724
     */
 
725
    for (cur = win->first_child ; cur != 0 ; cur = cur->nxt)
 
726
        cur->parent = 0;
 
727
 
 
728
    /* delete the window according to its type */
 
729
    switch(win->win_type)
 
730
    {
 
731
    case OS_BANNER_TYPE_TEXT:
 
732
        /* get the text window subclass data */
 
733
        twin = (osgen_txtwin_t *)win;
 
734
 
 
735
        /* delete its scrollback buffer, if it has one */
 
736
        if (twin->txtbuf != 0)
 
737
            osfree(twin->txtbuf);
 
738
 
 
739
        /* delete the line pointers */
 
740
        if (twin->line_ptr != 0)
 
741
            osfree(twin->line_ptr);
 
742
        break;
 
743
 
 
744
    case OS_BANNER_TYPE_TEXTGRID:
 
745
        /* get the grid window subclass data */
 
746
        gwin = (osgen_gridwin_t *)win;
 
747
 
 
748
        /* delete the character and color arrays */
 
749
        if (gwin->grid_txt != 0)
 
750
            osfree(gwin->grid_txt);
 
751
        if (gwin->grid_color != 0)
 
752
            osfree(gwin->grid_color);
 
753
        break;
 
754
    }
 
755
 
 
756
    /* delete the window itself */
 
757
    osfree(win);
 
758
}
 
759
 
 
760
/*
 
761
 *   Delete a window and all of its children 
 
762
 */
 
763
static void osgen_delete_win_tree(osgen_win_t *win)
 
764
{
 
765
    osgen_win_t *chi;
 
766
    osgen_win_t *nxt;
 
767
 
 
768
    /* delete the children first */
 
769
    for (chi = win->first_child ; chi != 0 ; chi = nxt)
 
770
    {
 
771
        /* remember the next one before we delete the current one */
 
772
        nxt = chi->nxt;
 
773
 
 
774
        /* delete this one */
 
775
        osgen_delete_win_tree(chi);
 
776
    }
 
777
 
 
778
    /* delete this window */
 
779
    osgen_delete_win(win);
 
780
}
 
781
 
 
782
/*
 
783
 *   Create a window and link it into our list.  We initialize the window
 
784
 *   and allocate its display buffer, but we do NOT set the window's size or
 
785
 *   position on the screen.  
 
786
 */
 
787
static osgen_win_t *osgen_create_win(int win_type, int where, void *other,
 
788
                                     osgen_win_t *parent)
 
789
{
 
790
    osgen_win_t *win;
 
791
    osgen_win_t *prv;
 
792
    osgen_win_t *cur;
 
793
    size_t struct_siz;
 
794
 
 
795
    /* figure the structure size based on the window type */
 
796
    switch(win_type)
 
797
    {
 
798
    case OS_BANNER_TYPE_TEXT:
 
799
        struct_siz = sizeof(osgen_txtwin_t);
 
800
        break;
 
801
 
 
802
    case OS_BANNER_TYPE_TEXTGRID:
 
803
        struct_siz = sizeof(osgen_gridwin_t);
 
804
        break;
 
805
 
 
806
    default:
 
807
        /* unrecognized type - return failure */
 
808
        return 0;
 
809
    }
 
810
 
 
811
    /* create the window object */
 
812
    win = (osgen_win_t *)osmalloc(struct_siz);
 
813
    if (win == 0)
 
814
        return 0;
 
815
 
 
816
    /* remember the type */
 
817
    win->win_type = win_type;
 
818
 
 
819
    /* it's not in a window list yet */
 
820
    win->nxt = 0;
 
821
 
 
822
    /* initialize with default colors */
 
823
    win->txtfg = OSGEN_COLOR_TEXT;
 
824
    win->txtbg = OSGEN_COLOR_TRANSPARENT;
 
825
    win->txtattr = 0;
 
826
    win->fillcolor = OSGEN_COLOR_TEXTBG;
 
827
 
 
828
    /* cache the oss translation of the fill color */
 
829
    win->oss_fillcolor = ossgetcolor(OSGEN_COLOR_TEXT, OSGEN_COLOR_TEXTBG,
 
830
                                     0, 0);
 
831
 
 
832
    /* start at the upper left corner */
 
833
    win->x = 0;
 
834
    win->y = 0;
 
835
    win->scrollx = 0;
 
836
    win->scrolly = 0;
 
837
 
 
838
    /* 
 
839
     *   the window's position on screen will eventually be set by
 
840
     *   osgen_recalc_layout(), but initialize the position to a reasonable
 
841
     *   value for now in case anyone looks at it before then 
 
842
     */
 
843
    win->winx = 0;
 
844
    win->winy = 0;
 
845
 
 
846
    /* we haven't seen any text in the window yet */
 
847
    win->xmax = 0;
 
848
    win->ymax = 0;
 
849
 
 
850
    /* clear the flags */
 
851
    win->flags = 0;
 
852
 
 
853
    /* remember our parent */
 
854
    win->parent = parent;
 
855
 
 
856
    /* we have no children yet */
 
857
    win->first_child = 0;
 
858
 
 
859
    /* if there's a parent, insert the window into the parent's child list */
 
860
    if (parent != 0)
 
861
    {
 
862
        /* insert into the parent's child list at the proper point */
 
863
        switch(where)
 
864
        {
 
865
        case OS_BANNER_FIRST:
 
866
            /* link it at the head of the list */
 
867
            win->nxt = parent->first_child;
 
868
            parent->first_child = win;
 
869
            break;
 
870
 
 
871
        case OS_BANNER_LAST:
 
872
        default:
 
873
            /* find the end of the parent's list */
 
874
            for (cur = parent->first_child ; cur != 0 && cur->nxt != 0 ;
 
875
                 cur = cur->nxt) ;
 
876
 
 
877
            /* link it after the last element */
 
878
            win->nxt = 0;
 
879
            if (cur != 0)
 
880
                cur->nxt = win;
 
881
            else
 
882
                parent->first_child = win;
 
883
 
 
884
            /* done */
 
885
            break;
 
886
 
 
887
        case OS_BANNER_BEFORE:
 
888
        case OS_BANNER_AFTER:
 
889
            /* scan the parent's child list, looking for 'other' */
 
890
            for (prv = 0, cur = parent->first_child ;
 
891
                 cur != 0 && cur != other ;
 
892
                 prv = cur, cur = cur->nxt) ;
 
893
 
 
894
            /* 
 
895
             *   if we didn't find 'other', link the new window at the tail
 
896
             *   of the list by default; since 'prv' will be the last item if
 
897
             *   we didn't find 'other', we can simply set the link mode to
 
898
             *   'before' to link before the placeholder null at the end of
 
899
             *   the list 
 
900
             */
 
901
            if (cur == 0)
 
902
                where = OS_BANNER_BEFORE;
 
903
 
 
904
            /* if we're linking after 'cur', advance one position */
 
905
            if (where == OS_BANNER_AFTER)
 
906
            {
 
907
                prv = cur;
 
908
                cur = cur->nxt;
 
909
            }
 
910
 
 
911
            /* link before 'cur', which is right after 'prv' */
 
912
            win->nxt = cur;
 
913
            if (prv != 0)
 
914
                prv->nxt = win;
 
915
            else
 
916
                parent->first_child = win;
 
917
 
 
918
            /* done */
 
919
            break;
 
920
        }
 
921
    }
 
922
 
 
923
    /* return the new window */
 
924
    return win;
 
925
}
 
926
 
 
927
/*
 
928
 *   Create a text window 
 
929
 */
 
930
static osgen_txtwin_t *osgen_create_txtwin(int where, void *other,
 
931
                                           void *parent,
 
932
                                           unsigned int buf_size,
 
933
                                           unsigned int buf_lines)
 
934
{
 
935
    osgen_txtwin_t *win;
 
936
    
 
937
    /* create the base window */
 
938
    win = (osgen_txtwin_t *)osgen_create_win(OS_BANNER_TYPE_TEXT,
 
939
                                             where, other, parent);
 
940
 
 
941
    /* if that failed, give up now */
 
942
    if (win == 0)
 
943
        return 0;
 
944
 
 
945
    /* allocate the window's buffer */
 
946
    win->txtbufsiz = buf_size;
 
947
    win->txtbuf = (char *)osmalloc(buf_size);
 
948
 
 
949
    /* allocate the line starts */
 
950
    win->line_ptr_cnt = buf_lines;
 
951
    win->line_ptr = (char **)osmalloc(buf_lines * sizeof(char *));
 
952
 
 
953
    /* make sure we allocated everything properly */
 
954
    if (win->txtbuf == 0 || win->line_ptr == 0)
 
955
    {
 
956
        /* free anything we allocated */
 
957
        osgen_delete_win(&win->base);
 
958
        
 
959
        /* return failure */
 
960
        return 0;
 
961
    }
 
962
 
 
963
    /* set up the buffer free pointer */
 
964
    win->txtfree = win->txtbuf;
 
965
 
 
966
    /* start out with a single line in the buffer */
 
967
    win->line_count = 1;
 
968
 
 
969
    /* set up the first line start */
 
970
    win->first_line = 0;
 
971
    win->line_ptr[0] = win->txtbuf;
 
972
 
 
973
    /* initialize the start-of-line colors */
 
974
    win->solfg = win->base.txtfg;
 
975
    win->solbg = win->base.txtbg;
 
976
    win->solattr = win->base.txtattr;
 
977
 
 
978
    /* start the new first line with the current text color */
 
979
    osssb_add_color_code(win);
 
980
 
 
981
    /* return the new window */
 
982
    return win;
 
983
}
 
984
 
 
985
/*
 
986
 *   Create a text grid window 
 
987
 */
 
988
static osgen_gridwin_t *osgen_create_gridwin(int where, void *other,
 
989
                                             void *parent,
 
990
                                             int grid_wid, int grid_ht)
 
991
{
 
992
    osgen_gridwin_t *win;
 
993
 
 
994
    /* create the base window */
 
995
    win = (osgen_gridwin_t *)osgen_create_win(OS_BANNER_TYPE_TEXTGRID,
 
996
                                              where, other, parent);
 
997
 
 
998
    /* if that failed, give up now */
 
999
    if (win == 0)
 
1000
        return 0;
 
1001
    
 
1002
    /* allocate the grid to the requested size */
 
1003
    win->grid_wid = grid_wid;
 
1004
    win->grid_ht = grid_ht;
 
1005
    win->grid_txt = (char *)osmalloc(grid_wid * grid_ht);
 
1006
    win->grid_color = (osgen_charcolor_t *)osmalloc(
 
1007
        grid_wid * grid_ht * sizeof(osgen_charcolor_t));
 
1008
 
 
1009
    /* if we failed to allocate, delete the window and abort */
 
1010
    if (win->grid_txt == 0 || win->grid_color == 0)
 
1011
    {
 
1012
        /* free anything we allocated */
 
1013
        osgen_delete_win(&win->base);
 
1014
        
 
1015
        /* return failure */
 
1016
        return 0;
 
1017
    }
 
1018
 
 
1019
    /* clear the grid text and color arrays */
 
1020
    osgen_gridwin_clear(win, 0, win->grid_wid * win->grid_ht);
 
1021
 
 
1022
    /* return the new window */
 
1023
    return win;
 
1024
}
 
1025
 
 
1026
/*
 
1027
 *   Clear a window 
 
1028
 */
 
1029
static void osgen_clear_win(osgen_win_t *win)
 
1030
{
 
1031
    osgen_txtwin_t *twin;
 
1032
    osgen_gridwin_t *gwin;
 
1033
 
 
1034
    /* check the type */
 
1035
    switch(win->win_type)
 
1036
    {
 
1037
    case OS_BANNER_TYPE_TEXT:
 
1038
        /* get the text window subclass data */
 
1039
        twin = (osgen_txtwin_t *)win;
 
1040
 
 
1041
        /* reset the window's scrollback buffer */
 
1042
        twin->txtfree = twin->txtbuf;
 
1043
        twin->line_count = 1;
 
1044
        twin->first_line = 0;
 
1045
        twin->line_ptr[0] = twin->txtbuf;
 
1046
 
 
1047
        /* start the new first line with the current text color */
 
1048
        osssb_add_color_code(twin);
 
1049
 
 
1050
        /* done */
 
1051
        break;
 
1052
 
 
1053
    case OS_BANNER_TYPE_TEXTGRID:
 
1054
        /* get the grid window subclass data */
 
1055
        gwin = (osgen_gridwin_t *)win;
 
1056
 
 
1057
        /* clear the entire grid */
 
1058
        osgen_gridwin_clear(gwin, 0, gwin->grid_wid * gwin->grid_ht);
 
1059
 
 
1060
        /* done */
 
1061
        break;
 
1062
    }
 
1063
 
 
1064
    /* put the cursor at the top left of the window */
 
1065
    win->x = 0;
 
1066
    win->y = 0;
 
1067
 
 
1068
    /* set the scrolling position to the top of the window */
 
1069
    win->scrollx = 0;
 
1070
    win->scrolly = 0;
 
1071
 
 
1072
    /* the content size is now zero */
 
1073
    win->xmax = 0;
 
1074
    win->ymax = 0;
 
1075
 
 
1076
    /* if the buffer was full, it's full no longer */
 
1077
    win->flags &= ~OSGEN_FULL;
 
1078
 
 
1079
    /*
 
1080
     *   If this window is not auto-scrolling, don't actually clear it
 
1081
     *   visually right now, but just set a deferred-redraw on the window;
 
1082
     *   clearing a window that doesn't automatically scroll usually means
 
1083
     *   that we're updating some live status information, so we use deferred
 
1084
     *   drawing on these windows to reduce flicker during updates.  If the
 
1085
     *   window automatically scrolls, then the caller presumably is
 
1086
     *   displaying more sizable data, and thus would want the user to see
 
1087
     *   the text output as it occurs.
 
1088
     *   
 
1089
     *   Note that both approaches (deferred vs immediate redraw) yield the
 
1090
     *   same results in the end, because we always stop deferring redraws
 
1091
     *   when we stop to ask for input or simply pause for a time delay; the
 
1092
     *   only difference is that deferred redrawing reduces flicker by
 
1093
     *   gathering all the updates into a single operation, while immediate
 
1094
     *   update might flicker more but shows individual bits of text output
 
1095
     *   as they occur.  
 
1096
     */
 
1097
    if (!(win->flags & OSGEN_AUTO_VSCROLL))
 
1098
    {
 
1099
        /* defer redrawing until we need to */
 
1100
        win->flags |= OSGEN_DEFER_REDRAW;
 
1101
    }
 
1102
    else
 
1103
    {
 
1104
        /* clear the window's on-screen area with its fill color */
 
1105
        ossclr(win->winy, win->winx,
 
1106
               win->winy + win->ht - 1, win->winx + win->wid - 1,
 
1107
               win->oss_fillcolor);
 
1108
    }
 
1109
}
 
1110
 
 
1111
/*
 
1112
 *   Display text in a window, all in a single color, truncating the display
 
1113
 *   if we start before the left edge of the window or go past the right
 
1114
 *   edge of the window.  The x,y coordinates are given in window
 
1115
 *   coordinates.  
 
1116
 */
 
1117
static void osgen_disp_trunc(osgen_win_t *win, int y, int x, int oss_color,
 
1118
                             const char *p)
 
1119
{
 
1120
    char buf[64];
 
1121
    size_t chars_rem;
 
1122
    size_t wid_rem;
 
1123
 
 
1124
    /* if we have deferred redrawing pending, don't bother drawing now */
 
1125
    if (S_deferred_redraw || (win->flags & OSGEN_DEFER_REDRAW) != 0)
 
1126
        return;
 
1127
 
 
1128
    /* get the number of characters to be displayed */
 
1129
    chars_rem = strlen(p);
 
1130
 
 
1131
    /* calculate the amount of space we have to work with on the screen */
 
1132
    wid_rem = win->wid - (x >= 0 ? x : 0);
 
1133
 
 
1134
    /* make sure at least some of the text overlaps the window */
 
1135
    if (y < 0 || y >= (int)win->ht
 
1136
        || x + (int)chars_rem <= 0 || x >= (int)win->wid)
 
1137
        return;
 
1138
 
 
1139
    /* 
 
1140
     *   if we're starting to the left of the window, skip characters up to
 
1141
     *   the first character visible in the window 
 
1142
     */
 
1143
    if (x < 0)
 
1144
    {
 
1145
        /* 
 
1146
         *   get the number of characters to skip - this is simply the
 
1147
         *   negative of the x position, since x position zero is the first
 
1148
         *   column to display 
 
1149
         */
 
1150
        x = -x;
 
1151
 
 
1152
        /* 
 
1153
         *   if we don't have enough characters to reach the left edge of
 
1154
         *   the window, we have nothing to do 
 
1155
         */
 
1156
        if (chars_rem <= (size_t)x)
 
1157
            return;
 
1158
 
 
1159
        /* skip the desired number of characters */
 
1160
        chars_rem -= x;
 
1161
        p += x;
 
1162
 
 
1163
        /* we've skipped up to column zero, so proceed from there */
 
1164
        x = 0;
 
1165
    }
 
1166
 
 
1167
    /* if the text entirely fits, display it and we're done */
 
1168
    if (chars_rem < wid_rem)
 
1169
    {
 
1170
        /* display the entire string */
 
1171
        ossdsp(win->winy + y, win->winx + x, oss_color, p);
 
1172
 
 
1173
        /* done */
 
1174
        return;
 
1175
    }
 
1176
 
 
1177
    /* 
 
1178
     *   we have too much to display, so display as much as will fit - keep
 
1179
     *   going until we run out of space for the display (we know we'll run
 
1180
     *   out of space before we run out of text, because we know we have too
 
1181
     *   much text to fit) 
 
1182
     */
 
1183
    while (wid_rem != 0)
 
1184
    {
 
1185
        size_t cur;
 
1186
 
 
1187
        /* display as much as will fit, up to our buffer length */
 
1188
        cur = wid_rem;
 
1189
        if (cur > sizeof(buf) - 1)
 
1190
            cur = sizeof(buf) - 1;
 
1191
 
 
1192
        /* copy this portion to our buffer and null-terminate it */
 
1193
        memcpy(buf, p, cur);
 
1194
        buf[cur] = '\0';
 
1195
 
 
1196
        /* display it */
 
1197
        ossdsp(win->winy + y, win->winx + x, oss_color, buf);
 
1198
 
 
1199
        /* advance our screen position */
 
1200
        x += cur;
 
1201
 
 
1202
        /* advance past the source material we just displayed */
 
1203
        p += cur;
 
1204
 
 
1205
        /* deduct the on-screen space we consumed from the remaining space */
 
1206
        wid_rem -= cur;
 
1207
    }
 
1208
}
 
1209
 
 
1210
/* ------------------------------------------------------------------------ */
 
1211
/*
 
1212
 *   Initialize the scrollback buffer.  This initializes the main text area
 
1213
 *   window.
 
1214
 *   
 
1215
 *   The oss code should call this during initialization to set up the
 
1216
 *   osgen3-layer display management; note that this routine must be called
 
1217
 *   from the oss code AFTER the screen size has been determined and stored
 
1218
 *   in the global variables G_oss_screen_width and G_oss_screen_height.  
 
1219
 */
 
1220
void osssbini(unsigned int size)
 
1221
{
 
1222
    osgen_txtwin_t *win;
 
1223
 
 
1224
    /* allocate our main window if we're not in 'plain' mode */
 
1225
    if (!os_f_plain)
 
1226
    {
 
1227
        /* 
 
1228
         *   Initialize the main window.  The main window has no parent,
 
1229
         *   since it's the root of the window tree.  
 
1230
         */
 
1231
        S_main_win = win = osgen_create_txtwin(
 
1232
            OS_BANNER_FIRST, 0, 0, size, size/40);
 
1233
        if (win == 0)
 
1234
        {
 
1235
            /* show a message and give up */
 
1236
            printf("There is not enough memory to run this program.\n");
 
1237
            exit(1);
 
1238
        }
 
1239
        
 
1240
        /* make this the default window */
 
1241
        S_default_win = win;
 
1242
        
 
1243
        /* scroll to keep the cursor visible when writing to this window */
 
1244
        win->base.flags |= OSGEN_AUTO_VSCROLL;
 
1245
        
 
1246
        /* initially give the window the entire screen */
 
1247
        win->base.winy = 0;
 
1248
        win->base.winx = 0;
 
1249
        win->base.wid = G_oss_screen_width;
 
1250
        win->base.ht = G_oss_screen_height;
 
1251
        
 
1252
        /* set the initial page size */
 
1253
        G_os_linewidth = win->base.wid;
 
1254
        G_os_pagelength = (win->base.ht > 2
 
1255
                           ? win->base.ht - 2
 
1256
                           : win->base.ht > 1
 
1257
                           ? win->base.ht - 1 : win->base.ht);
 
1258
 
 
1259
        /* set the initial color scheme to the default text color */
 
1260
        os_set_text_color(OS_COLOR_P_TEXT, OS_COLOR_P_TRANSPARENT);
 
1261
        os_set_screen_color(OS_COLOR_P_TEXTBG);
 
1262
 
 
1263
        /* clear the window */
 
1264
        osgen_clear_win(&win->base);
 
1265
    }
 
1266
    else
 
1267
    {
 
1268
        /* use the entire available display */
 
1269
        G_os_linewidth = G_oss_screen_width;
 
1270
        G_os_pagelength = G_oss_screen_height - 2;
 
1271
    }
 
1272
}
 
1273
 
 
1274
/*
 
1275
 *   Delete the scrollback buffer.  The oss code should call this during
 
1276
 *   program termination to free memory allocated by the osgen3 layer.  
 
1277
 */
 
1278
void osssbdel(void)
 
1279
{
 
1280
    /* delete the main window and its children */
 
1281
    if (S_main_win != 0)
 
1282
        osgen_delete_win_tree(&S_main_win->base);
 
1283
}
 
1284
 
 
1285
/* ------------------------------------------------------------------------ */
 
1286
/*
 
1287
 *   Scrollback operations.  Scrollback applies only to ordinary text
 
1288
 *   windows.  
 
1289
 */
 
1290
 
 
1291
/*
 
1292
 *   advance a pointer in a scrollback buffer 
 
1293
 */
 
1294
static char *ossadvsp(osgen_txtwin_t *win, char *p)
 
1295
{
 
1296
    /* move to the next byte */
 
1297
    ++p;
 
1298
 
 
1299
    /* if we've passed the end of the buffer, wrap to the beginning */
 
1300
    if (p >= win->txtbuf + win->txtbufsiz)
 
1301
        p = win->txtbuf;
 
1302
 
 
1303
    /* return the pointer */
 
1304
    return p;
 
1305
}
 
1306
 
 
1307
/*
 
1308
 *   decrement a scrollback buffer pointer 
 
1309
 */
 
1310
static char *ossdecsp(osgen_txtwin_t *win, char *p)
 
1311
{
 
1312
    /* if we're at the start of the buffer, wrap to just past the end */
 
1313
    if (p == win->txtbuf)
 
1314
        p = win->txtbuf + win->txtbufsiz;
 
1315
 
 
1316
    /* move to the previous byte */
 
1317
    --p;
 
1318
 
 
1319
    /* return the pointer */
 
1320
    return p;
 
1321
}
 
1322
 
 
1323
/*
 
1324
 *   Get the line pointer for the given line 
 
1325
 */
 
1326
static char **osgen_get_line_ptr(osgen_txtwin_t *win, int y)
 
1327
{
 
1328
    size_t idx;
 
1329
 
 
1330
    /* if the line doesn't exist, return null */
 
1331
    if (y < 0 || (size_t)y >= win->line_count)
 
1332
        return 0;
 
1333
 
 
1334
    /* add the line number to the base index to get the linear index */
 
1335
    idx = y + win->first_line;
 
1336
 
 
1337
    /* wrap back to the start of the array if necessary */
 
1338
    if (idx >= win->line_ptr_cnt)
 
1339
        idx -= win->line_ptr_cnt;
 
1340
 
 
1341
    /* return the line at this index */
 
1342
    return &win->line_ptr[idx];
 
1343
}
 
1344
 
 
1345
/*
 
1346
 *   Get a pointer to the text of the line in the given window at the given
 
1347
 *   document y coordinate.  
 
1348
 */
 
1349
static char *osgen_get_line(osgen_txtwin_t *win, int y)
 
1350
{
 
1351
    char **line_ptr;
 
1352
 
 
1353
    /* get the line pointer for the given line */
 
1354
    line_ptr = osgen_get_line_ptr(win, y);
 
1355
 
 
1356
    /* return the text it points to, or null if there's no such line */
 
1357
    return (line_ptr != 0 ? *line_ptr : 0);
 
1358
}
 
1359
 
 
1360
/*
 
1361
 *   Add a byte to the scrollback buffer.  Returns true if the byte was
 
1362
 *   successfully added, false if not.  
 
1363
 */
 
1364
static int osssb_add_byte(osgen_txtwin_t *win, char c)
 
1365
{
 
1366
    char *nxtfree;
 
1367
 
 
1368
    /* if the window is full, indicate failure */
 
1369
    if ((win->base.flags & OSGEN_FULL) != 0)
 
1370
        return FALSE;
 
1371
 
 
1372
    /* 
 
1373
     *   If there's no room, and we're not in auto-vscroll mode, simply
 
1374
     *   discard the text.  A window that is not in auto-vscroll mode is
 
1375
     *   designed to always show the oldest text, so if we overflow the
 
1376
     *   buffer, then we want to start discarding the newest test.  
 
1377
     */
 
1378
    nxtfree = ossadvsp(win, win->txtfree);
 
1379
    if (nxtfree == win->line_ptr[win->first_line]
 
1380
        && !(win->base.flags & OSGEN_AUTO_VSCROLL))
 
1381
    {
 
1382
        /* 
 
1383
         *   The buffer is full, and we're not in auto-vscroll mode -
 
1384
         *   discard the new text.  Before we do, set the current last byte
 
1385
         *   to null, to terminate the current line at this point.  
 
1386
         */
 
1387
        *win->txtfree = '\0';
 
1388
 
 
1389
        /* set the 'full' flag */
 
1390
        win->base.flags |= OSGEN_FULL;
 
1391
 
 
1392
        /* indicate failure */
 
1393
        return FALSE;
 
1394
    }
 
1395
 
 
1396
    /* add the character to the buffer */
 
1397
    *win->txtfree = c;
 
1398
 
 
1399
    /* advance the free pointer */
 
1400
    win->txtfree = nxtfree;
 
1401
 
 
1402
    /* 
 
1403
     *   if the free pointer has just collided with the start of the oldest
 
1404
     *   line, delete the oldest line 
 
1405
     */
 
1406
    if (win->txtfree == win->line_ptr[win->first_line])
 
1407
    {
 
1408
        /* delete the oldest line to make room for the new data */
 
1409
        win->first_line++;
 
1410
        if (win->first_line >= win->line_ptr_cnt)
 
1411
            win->first_line = 0;
 
1412
 
 
1413
        /* we have one less line in the buffer */
 
1414
        win->line_count--;
 
1415
 
 
1416
        /* adjust our document cooordinates for the loss of a line */
 
1417
        win->base.y--;
 
1418
 
 
1419
        /* 
 
1420
         *   Adjust the scrolling position for the loss of a line.  If the
 
1421
         *   window is displaying the line that we just deleted (i.e., the
 
1422
         *   window's vertical scroll offset is zero), we must redraw the
 
1423
         *   window; otherwise, the change won't affect the display, so we
 
1424
         *   can merely adjust the scrolling offset.  
 
1425
         */
 
1426
        if (win->base.scrolly == 0)
 
1427
            win->base.flags |= OSGEN_DEFER_REDRAW;
 
1428
        else
 
1429
            win->base.scrolly--;
 
1430
    }
 
1431
 
 
1432
    /* we successfully added the byte */
 
1433
    return TRUE;
 
1434
}
 
1435
 
 
1436
/*
 
1437
 *   Add a given number of bytes.  The bytes will be added as a unit - if we
 
1438
 *   can't add all of the bytes, we won't add any of them.  
 
1439
 */
 
1440
static int osssb_add_bytes(osgen_txtwin_t *win, const char *p, size_t len)
 
1441
{
 
1442
    char *orig_free;
 
1443
 
 
1444
    /* note the original free pointer */
 
1445
    orig_free = win->txtfree;
 
1446
 
 
1447
    /* add the bytes */
 
1448
    for ( ; len != 0 ; --len)
 
1449
    {
 
1450
        /* try adding this byte */
 
1451
        if (!osssb_add_byte(win, *p++))
 
1452
        {
 
1453
            /* 
 
1454
             *   failure - forget everything we've added by resetting the
 
1455
             *   free pointer to its original value on entry 
 
1456
             */
 
1457
            win->txtfree = orig_free;
 
1458
 
 
1459
            /* indicate failure */
 
1460
            return FALSE;
 
1461
        }
 
1462
    }
 
1463
 
 
1464
    /* we added all of the bytes successfully */
 
1465
    return TRUE;
 
1466
}
 
1467
 
 
1468
/*
 
1469
 *   Add a "safety" null byte.  This adds a null byte at the end of the
 
1470
 *   buffer, in case we need to inspect the current line before we add any
 
1471
 *   more text.  
 
1472
 */
 
1473
static void osssb_add_safety_null(osgen_txtwin_t *win)
 
1474
{
 
1475
    /* add the null */
 
1476
    if (osssb_add_byte(win, '\0'))
 
1477
    {
 
1478
        /* 
 
1479
         *   we added the null - decrement the free pointer so we overwrite
 
1480
         *   the null byte if we add any more text to the current line 
 
1481
         */
 
1482
        win->txtfree = ossdecsp(win, win->txtfree);
 
1483
    }
 
1484
}
 
1485
 
 
1486
/*
 
1487
 *   Add an appropriate color code to the scrollback buffer to yield the
 
1488
 *   current color setting.  
 
1489
 */
 
1490
static void osssb_add_color_code(osgen_txtwin_t *win)
 
1491
{
 
1492
    char buf[10];
 
1493
 
 
1494
    /* if we have any attributes, add an attribute code */
 
1495
    if (win->base.txtattr != 0)
 
1496
    {
 
1497
        /* add the attribute code sequence */
 
1498
        buf[0] = OSGEN_ATTR;
 
1499
        buf[1] = (char)win->base.txtattr;
 
1500
        osssb_add_bytes(win, buf, 2);
 
1501
    }
 
1502
 
 
1503
    /* if we're using the plain text color, add a color code */
 
1504
    if (win->base.txtfg != OSGEN_COLOR_TEXT
 
1505
        || win->base.txtbg != OSGEN_COLOR_TRANSPARENT)
 
1506
    {
 
1507
        /* add the explicit color code */
 
1508
        buf[0] = OSGEN_COLOR;
 
1509
        buf[1] = win->base.txtfg;
 
1510
        buf[2] = win->base.txtbg;
 
1511
        osssb_add_bytes(win, buf, 3);
 
1512
    }
 
1513
 
 
1514
    /* add a safety null terminator */
 
1515
    osssb_add_safety_null(win);
 
1516
}
 
1517
 
 
1518
/*
 
1519
 *   Start a new line in the scrollback buffer.  Returns true on success,
 
1520
 *   false if there's no space to add the newline character.  
 
1521
 */
 
1522
static int osssb_new_line(osgen_txtwin_t *win)
 
1523
{
 
1524
    char **line_ptr;
 
1525
 
 
1526
    /* add a null byte to mark the end of the current line */
 
1527
    if (!osssb_add_byte(win, '\0'))
 
1528
        return FALSE;
 
1529
 
 
1530
    /* if the window is full, indicate failure */
 
1531
    if ((win->base.flags & OSGEN_FULL) != 0)
 
1532
        return FALSE;
 
1533
 
 
1534
    /* 
 
1535
     *   If the line buffer is full, and this window doesn't automatically
 
1536
     *   scroll vertically, drop the new text.  When we're not in
 
1537
     *   auto-vscroll mode, we drop text from the end of the buffer rather
 
1538
     *   than from the beginning.  
 
1539
     */
 
1540
    if (win->line_count + 1 > win->line_ptr_cnt
 
1541
        && !(win->base.flags & OSGEN_AUTO_VSCROLL))
 
1542
    {
 
1543
        /* set the 'full' flag */
 
1544
        win->base.flags |= OSGEN_FULL;
 
1545
 
 
1546
        /* indicate failure */
 
1547
        return FALSE;
 
1548
    }
 
1549
 
 
1550
    /* move to the left column */
 
1551
    win->base.x = 0;
 
1552
 
 
1553
    /* add a line start */
 
1554
    if (win->line_count >= win->line_ptr_cnt)
 
1555
    {
 
1556
        /* forget the original first line */
 
1557
        win->first_line++;
 
1558
        if (win->first_line == win->line_ptr_cnt)
 
1559
            win->first_line = 0;
 
1560
 
 
1561
        /* 
 
1562
         *   Since we took away one line and we're adding another, the total
 
1563
         *   line count and the document 'y' position aren't changing - but
 
1564
         *   the line at the document 'y' position now belongs to the new
 
1565
         *   last line.
 
1566
         *   
 
1567
         *   Adjust the scrolling position for the loss of a line at the
 
1568
         *   start of the buffer.  This doesn't change what we're displaying
 
1569
         *   on the screen, because we're simply reducing the amount of text
 
1570
         *   in the buffer before the top of the screen, so we must
 
1571
         *   compensate by decrementing the document coordinate of the top
 
1572
         *   line in the window.  However, if the line we're taking away is
 
1573
         *   showing in the window, then we do need to redraw the contents,
 
1574
         *   because the change will be visible; set the deferred redraw
 
1575
         *   flag for the window in this case so that we eventually redraw
 
1576
         *   the whole thing.  
 
1577
         */
 
1578
        if (win->base.scrolly == 0)
 
1579
            win->base.flags |= OSGEN_DEFER_REDRAW;
 
1580
        else
 
1581
            win->base.scrolly--;
 
1582
    }
 
1583
    else
 
1584
    {
 
1585
        /* count the new line */
 
1586
        win->line_count++;
 
1587
 
 
1588
        /* note the highest output 'y' position if this is it */
 
1589
        if (win->base.y > win->base.ymax)
 
1590
            win->base.ymax = win->base.y;
 
1591
 
 
1592
        /* increase the document y position for the new line */
 
1593
        win->base.y++;
 
1594
    }
 
1595
 
 
1596
    /* get the line pointer for the new last line */
 
1597
    line_ptr = osgen_get_line_ptr(win, win->base.y);
 
1598
 
 
1599
    /* 
 
1600
     *   set the new line pointer to point to the first free byte of the
 
1601
     *   buffer 
 
1602
     */
 
1603
    *line_ptr = win->txtfree;
 
1604
 
 
1605
    /* add a color code to the start of the new line, if necessary */
 
1606
    osssb_add_color_code(win);
 
1607
 
 
1608
    /* remember the text color at the start of this line */
 
1609
    win->solfg = win->base.txtfg;
 
1610
    win->solbg = win->base.txtbg;
 
1611
    win->solattr = win->base.txtattr;
 
1612
 
 
1613
    /* add a safety null terminator */
 
1614
    osssb_add_safety_null(win);
 
1615
 
 
1616
    /* success */
 
1617
    return TRUE;
 
1618
}
 
1619
 
 
1620
/*
 
1621
 *   Scroll the window forward by the given number of lines 
 
1622
 */
 
1623
static void osgen_scroll_win_fwd(osgen_txtwin_t *win, size_t lines)
 
1624
{
 
1625
    /* limit the scrolling to the available number of lines */
 
1626
    if (lines > win->line_count - win->base.scrolly - win->base.ht)
 
1627
        lines = win->line_count - win->base.scrolly - win->base.ht;
 
1628
 
 
1629
    /* 
 
1630
     *   If we're scrolling more than a few lines, or more than the entire
 
1631
     *   window's height, just redraw the entire window.  If it's a small
 
1632
     *   number of lines, scroll the screen one line at a time, so that we
 
1633
     *   don't have to redraw as much.  
 
1634
     */
 
1635
    if (lines > 3 || (int)lines >= win->base.ht)
 
1636
    {
 
1637
        /* adjust the scroll position */
 
1638
        win->base.scrolly += lines;
 
1639
 
 
1640
        /* redraw the entire window */
 
1641
        osgen_redraw_win(&win->base);
 
1642
    }
 
1643
    else
 
1644
    {
 
1645
        /* scroll one line at a time */
 
1646
        for ( ; lines != 0 ; --lines)
 
1647
        {
 
1648
            /* scroll the window's on-screen area one line */
 
1649
            ossscr(win->base.winy, win->base.winx,
 
1650
                   win->base.winy + win->base.ht - 1,
 
1651
                   win->base.winx + win->base.wid - 1,
 
1652
                   win->base.oss_fillcolor);
 
1653
 
 
1654
            /* adjust the scroll position */
 
1655
            win->base.scrolly++;
 
1656
 
 
1657
            /* redraw the bottom line */
 
1658
            osgen_scrdisp(&win->base, 0, win->base.ht - 1, win->base.wid);
 
1659
        }
 
1660
    }
 
1661
}
 
1662
 
 
1663
/*
 
1664
 *   Scroll the window back by the given number of lines 
 
1665
 */
 
1666
static void osgen_scroll_win_back(osgen_txtwin_t *win, int lines)
 
1667
{
 
1668
    /* limit scrolling to the available number of lines above the window */
 
1669
    if (lines > win->base.scrolly)
 
1670
        lines = win->base.scrolly;
 
1671
 
 
1672
    /* 
 
1673
     *   If we're scrolling more than a few lines, or by the full window
 
1674
     *   height or more, just redraw the entire window.  Otherwise, scroll a
 
1675
     *   line at a time to minimize redrawing.  
 
1676
     */
 
1677
    if (lines > 3)
 
1678
    {
 
1679
        /* adjust the scroll position */
 
1680
        win->base.scrolly -= lines;
 
1681
 
 
1682
        /* redraw the entire window */
 
1683
        osgen_redraw_win(&win->base);
 
1684
    }
 
1685
    else
 
1686
    {
 
1687
        /* scroll up by one line at a time */
 
1688
        for ( ; lines != 0 ; --lines)
 
1689
        {
 
1690
            /* scroll the window's on-screen area one line */
 
1691
            ossscu(win->base.winy, win->base.winx,
 
1692
                   win->base.winy + win->base.ht - 1,
 
1693
                   win->base.winx + win->base.wid - 1,
 
1694
                   win->base.oss_fillcolor);
 
1695
 
 
1696
            /* adjust the scroll position */
 
1697
            win->base.scrolly--;
 
1698
 
 
1699
            /* redraw the top line */
 
1700
            osgen_scrdisp(&win->base, 0, 0, win->base.wid);
 
1701
        }
 
1702
    }
 
1703
}
 
1704
 
 
1705
/*
 
1706
 *   If a window is in auto-vscroll mode, bring the cursor into view in the
 
1707
 *   window. 
 
1708
 */
 
1709
static void osgen_auto_vscroll(osgen_txtwin_t *win)
 
1710
{
 
1711
    /* if the window isn't in auto-vscroll mode, ignore this */
 
1712
    if (!(win->base.flags & OSGEN_AUTO_VSCROLL))
 
1713
        return;
 
1714
    
 
1715
    /* 
 
1716
     *   scroll the window forward if the cursor is outside the visible area
 
1717
     *   of the window 
 
1718
     */
 
1719
    if (win->base.y >= win->base.scrolly + (int)win->base.ht)
 
1720
        osgen_scroll_win_fwd(win, win->base.y
 
1721
                             - (win->base.scrolly + win->base.ht) + 1);
 
1722
}
 
1723
 
 
1724
/*
 
1725
 *   Add text to the scrollback buffer. 
 
1726
 */
 
1727
static void ossaddsb(osgen_txtwin_t *win, const char *p, size_t len, int draw)
 
1728
{
 
1729
    int startx;
 
1730
    int starty;
 
1731
    int line_len;
 
1732
 
 
1733
    /* if there's no scrollback buffer, ignore it */
 
1734
    if (win->txtbuf == 0)
 
1735
        return;
 
1736
 
 
1737
    /* note the starting x,y position, for redrawing purposes */
 
1738
    startx = win->base.x - win->base.scrollx;
 
1739
    starty = win->base.y - win->base.scrolly;
 
1740
 
 
1741
    /* we haven't added any characters to the line yet */
 
1742
    line_len = 0;
 
1743
 
 
1744
    /*
 
1745
     *   Copy the text into the screen buffer, respecting the circular
 
1746
     *   nature of the screen buffer.  If the given text wraps lines,
 
1747
     *   enter an explicit carriage return into the text to ensure that
 
1748
     *   users can correctly count lines.
 
1749
     */
 
1750
    while (len != 0)
 
1751
    {
 
1752
        /* check what we have */
 
1753
        switch(*p)
 
1754
        {
 
1755
        case OSGEN_ATTR:
 
1756
            /* switch to the new attributes */
 
1757
            win->base.txtattr = (unsigned char)*(p+1);
 
1758
 
 
1759
            /* 
 
1760
             *   if we're at the start of the line, this is the start-of-line
 
1761
             *   attribute - this will ensure that if we back up with '\r',
 
1762
             *   we'll re-apply this attribute change 
 
1763
             */
 
1764
            if (win->base.x == 0)
 
1765
                win->solattr = win->base.txtattr;
 
1766
 
 
1767
            /* add the two-byte sequence */
 
1768
            osssb_add_bytes(win, p, 2);
 
1769
            p += 2;
 
1770
            len -= 2;
 
1771
            break;
 
1772
 
 
1773
        case OSGEN_COLOR:
 
1774
            /* switch to the new colors */
 
1775
            win->base.txtfg = (unsigned char)*(p+1);
 
1776
            win->base.txtbg = (unsigned char)*(p+2);
 
1777
 
 
1778
            /* 
 
1779
             *   if we're at the start of the line, this is the new
 
1780
             *   start-of-line color 
 
1781
             */
 
1782
            if (win->base.x == 0)
 
1783
            {
 
1784
                win->solfg = win->base.txtfg;
 
1785
                win->solbg = win->base.txtbg;
 
1786
            }
 
1787
 
 
1788
            /* add and skip the three-byte color sequence */
 
1789
            osssb_add_bytes(win, p, 3);
 
1790
            p += 3;
 
1791
            len -= 3;
 
1792
            break;
 
1793
            
 
1794
        case '\n':
 
1795
            /* add the new line */
 
1796
            osssb_new_line(win);
 
1797
 
 
1798
            /* draw the part of the line we added */
 
1799
            if (draw)
 
1800
                osgen_scrdisp(&win->base, startx, starty, line_len);
 
1801
 
 
1802
            /* skip this character */
 
1803
            ++p;
 
1804
            --len;
 
1805
 
 
1806
            /* reset to the next line */
 
1807
            startx = win->base.x - win->base.scrollx;
 
1808
            ++starty;
 
1809
            line_len = 0;
 
1810
 
 
1811
            /* bring the cursor onto the screen if auto-vscrolling */
 
1812
            if (draw)
 
1813
                osgen_auto_vscroll(win);
 
1814
 
 
1815
            /* done */
 
1816
            break;
 
1817
 
 
1818
        case '\r':
 
1819
            /*
 
1820
             *   We have a plain carriage return, which indicates that we
 
1821
             *   should go back to the start of the current line and
 
1822
             *   overwrite it.  (This is most likely to occur for the
 
1823
             *   "[More]" prompt.)
 
1824
             */
 
1825
 
 
1826
            /* set the free pointer back to the start of the current line */
 
1827
            win->txtfree = osgen_get_line(win, win->base.y);
 
1828
 
 
1829
            /* switch back to the color as of the start of the line */
 
1830
            win->base.txtfg = win->solfg;
 
1831
            win->base.txtbg = win->solbg;
 
1832
            win->base.txtattr = win->solattr;
 
1833
 
 
1834
            /* 
 
1835
             *   since we're back at the start of the line, add a color code
 
1836
             *   if necessary 
 
1837
             */
 
1838
            osssb_add_color_code(win);
 
1839
 
 
1840
            /* clear the window area of the line we're overwriting */
 
1841
            if (draw && starty >= 0 && starty < (int)win->base.ht)
 
1842
                ossclr(win->base.winy + starty, win->base.winx,
 
1843
                       win->base.winy + starty,
 
1844
                       win->base.winx + win->base.wid - 1,
 
1845
                       win->base.oss_fillcolor);
 
1846
 
 
1847
            /* reset to the start of the line */
 
1848
            line_len = 0;
 
1849
            startx = 0;
 
1850
 
 
1851
            /* move to the left column */
 
1852
            win->base.x = 0;
 
1853
 
 
1854
            /* skip this character */
 
1855
            ++p;
 
1856
            --len;
 
1857
 
 
1858
            /* done */
 
1859
            break;
 
1860
 
 
1861
        default:
 
1862
            /* for anything else, simply store the byte in the buffer */
 
1863
            if (osssb_add_byte(win, *p))
 
1864
            {
 
1865
                /* note the maximum output position if appropriate */
 
1866
                if (win->base.x > win->base.xmax)
 
1867
                    win->base.xmax = win->base.x;
 
1868
 
 
1869
                /* adjust the output x position */
 
1870
                win->base.x++;
 
1871
 
 
1872
                /* count the ordinary character in the display length */
 
1873
                ++line_len;
 
1874
            }
 
1875
            
 
1876
            /* skip the character */
 
1877
            ++p;
 
1878
            --len;
 
1879
                
 
1880
            /* done */
 
1881
            break;
 
1882
        }
 
1883
    }
 
1884
 
 
1885
    /* 
 
1886
     *   Add a safety null terminator after each addition, in case we need
 
1887
     *   to look at the buffer before this line is finished.  
 
1888
     */
 
1889
    osssb_add_safety_null(win);
 
1890
 
 
1891
    /* display the added text */
 
1892
    if (draw)
 
1893
    {
 
1894
        /* show the added text */
 
1895
        osgen_scrdisp(&win->base, startx, starty, line_len);
 
1896
 
 
1897
        /* bring the cursor onto the screen if auto-vscrolling */
 
1898
        osgen_auto_vscroll(win);
 
1899
    }
 
1900
}
 
1901
 
 
1902
/*
 
1903
 *   Add an input line to the scrollback buffer.
 
1904
 */
 
1905
void ossaddsb_input(osgen_txtwin_t *win, char *p, int add_nl)
 
1906
{
 
1907
    size_t chars_rem;
 
1908
    
 
1909
    /* get the number of characters in the input to add */
 
1910
    chars_rem = strlen(p);
 
1911
 
 
1912
    /* 
 
1913
     *   add the input in chunks, wrapping whenever we reach the right edge
 
1914
     *   of the window 
 
1915
     */
 
1916
    while (chars_rem != 0)
 
1917
    {
 
1918
        size_t wid_rem;
 
1919
        size_t cur;
 
1920
        
 
1921
        /* if we're already past the edge, add a newline */
 
1922
        if (win->base.x >= (int)win->base.wid)
 
1923
            osssb_new_line(win);
 
1924
 
 
1925
        /* figure out how much space we have */
 
1926
        wid_rem = win->base.wid - win->base.x;
 
1927
 
 
1928
        /* add as much as we can, up to the width remaining */
 
1929
        cur = chars_rem;
 
1930
        if (cur > wid_rem)
 
1931
            cur = wid_rem;
 
1932
 
 
1933
        /* add the text */
 
1934
        ossaddsb(win, p, cur, FALSE);
 
1935
 
 
1936
        /* skip this chunk */
 
1937
        p += cur;
 
1938
        chars_rem -= cur;
 
1939
    }
 
1940
 
 
1941
    /* add a newline after the input if desired */
 
1942
    if (add_nl)
 
1943
        osssb_new_line(win);
 
1944
    else
 
1945
        osssb_add_safety_null(win);
 
1946
}
 
1947
 
 
1948
/* ------------------------------------------------------------------------ */
 
1949
/*
 
1950
 *   End scrollback mode. 
 
1951
 */
 
1952
static void osgen_sb_mode_end()
 
1953
{
 
1954
    osgen_txtwin_t *win = S_sbmode_win;
 
1955
    
 
1956
    /* restore our original scroll position */
 
1957
    if (win->base.scrolly > S_sbmode_orig_scrolly)
 
1958
        osgen_scroll_win_back(win, win->base.scrolly - S_sbmode_orig_scrolly);
 
1959
    else if (win->base.scrolly < S_sbmode_orig_scrolly)
 
1960
        osgen_scroll_win_fwd(win, S_sbmode_orig_scrolly - win->base.scrolly);
 
1961
 
 
1962
    /* 
 
1963
     *   adjust the input editing cursor position if the scrolling position
 
1964
     *   doesn't match the original position 
 
1965
     */
 
1966
    S_gets_y += S_sbmode_orig_scrolly - win->base.scrolly;
 
1967
    
 
1968
    /* 
 
1969
     *   restore our full window area, adding back in the top line that we
 
1970
     *   removed to make room for the mode line 
 
1971
     */
 
1972
    win->base.winy -= 1;
 
1973
    win->base.ht += 1;
 
1974
    win->base.scrolly -= 1;
 
1975
    
 
1976
    /* redraw the top line, where we drew the mode line */
 
1977
    ossclr(win->base.winy, win->base.winx,
 
1978
           win->base.winy, win->base.winx + win->base.wid - 1,
 
1979
           win->base.oss_fillcolor);
 
1980
    osgen_scrdisp(&win->base, 0, 0, win->base.wid);
 
1981
 
 
1982
    /* 
 
1983
     *   Delete the temporary copy of the input from the scrollback buffer.
 
1984
     *   To do this, restore the x,y position and free pointer, then delete
 
1985
     *   extra lines from the end of the buffer until the last line points
 
1986
     *   to the same text it did before we added the command input text.  
 
1987
     */
 
1988
    win->base.x = S_sbmode_orig_x;
 
1989
    win->txtfree = S_sbmode_orig_txtfree;
 
1990
    while (win->line_count > 1
 
1991
           && osgen_get_line_ptr(win, win->base.y) != S_sbmode_orig_last_line)
 
1992
    {
 
1993
        /* this is an added line - delete the line */
 
1994
        win->line_count--;
 
1995
        win->base.y--;
 
1996
    }
 
1997
 
 
1998
    /* we're no longer in scrollback mode */
 
1999
    S_sbmode_win = 0;
 
2000
}
 
2001
 
 
2002
/*
 
2003
 *   Draw the scrollback mode status line.  This is a line we draw at the top
 
2004
 *   of the window to give a visual indication that we're in scrollback mode.
 
2005
 *   (Actually, we draw this one line above the current top of the window,
 
2006
 *   because we shrink the window vertically by one line while it's in
 
2007
 *   scrollback mode specifically to make room for the mode line.)  
 
2008
 */
 
2009
static void osgen_draw_sb_mode_line(void)
 
2010
{
 
2011
    osgen_txtwin_t *win;
 
2012
    int oss_color;
 
2013
 
 
2014
    /* get the current scrollback window */
 
2015
    win = S_sbmode_win;
 
2016
 
 
2017
    /* there's nothing to do if there's no scrollback window */
 
2018
    if (win == 0)
 
2019
        return;
 
2020
 
 
2021
    /* get the color - use the reverse of the window's current color scheme */
 
2022
    oss_color = ossgetcolor(win->base.fillcolor, win->base.txtfg, 0,
 
2023
                            OSGEN_COLOR_TRANSPARENT);
 
2024
 
 
2025
    /* clear the area of the mode line */
 
2026
    ossclr(win->base.winy - 1, win->base.winx,
 
2027
           win->base.winy - 1, win->base.winx + win->base.wid - 1, oss_color);
 
2028
 
 
2029
    /* momentarily bring the mode line back into the window's area */
 
2030
    win->base.winy -= 1;
 
2031
    win->base.ht += 1;
 
2032
 
 
2033
    /* draw the mode line text */
 
2034
    osgen_disp_trunc(&win->base, 0, 0, oss_color, OS_SBSTAT);
 
2035
 
 
2036
    /* undo our temporary adjustment of the window size */
 
2037
    win->base.winy += 1;
 
2038
    win->base.ht -= 1;
 
2039
}
 
2040
 
 
2041
/*
 
2042
 *   Process the event in scrollback mode.  If we're not already in
 
2043
 *   scrollback mode, we'll enter scrollback mode; otherwise, we'll process
 
2044
 *   the event in the already running scrollback.
 
2045
 *   
 
2046
 *   Returns true if we processed the event, false if we have terminated
 
2047
 *   scrollback mode and want the caller to process the event as it normally
 
2048
 *   would outside of scrollback mode.  
 
2049
 */
 
2050
static int osgen_sb_mode(osgen_txtwin_t *win, int event_type,
 
2051
                         os_event_info_t *event_info)
 
2052
{
 
2053
    int ret;
 
2054
    char c;
 
2055
    int just_started;
 
2056
 
 
2057
    /* if we were doing scrollback in a different window, cancel it */
 
2058
    if (S_sbmode_win != 0 && S_sbmode_win != win)
 
2059
        osgen_sb_mode_end();
 
2060
 
 
2061
    /* note if we're just entering scrollback mode */
 
2062
    just_started = (S_sbmode_win == 0);
 
2063
 
 
2064
    /* if scrollback mode isn't already in progress, set it up */
 
2065
    if (just_started)
 
2066
    {
 
2067
        /* if there's no buffer, we can't enter scrollback mode */
 
2068
        if (win->txtbuf == 0)
 
2069
            return FALSE;
 
2070
 
 
2071
        /* 
 
2072
         *   If the number of lines in the scrollback buffer doesn't exceed
 
2073
         *   the number of lines displayed in the window, then don't bother
 
2074
         *   entering scrollback mode.  If the window isn't at least two
 
2075
         *   lines high, don't allow it either, as we need space to draw our
 
2076
         *   mode line.  
 
2077
         */
 
2078
        if (win->line_count <= win->base.ht || win->base.ht < 2)
 
2079
            return FALSE;
 
2080
 
 
2081
        /* remember the window involved in scrollback */
 
2082
        S_sbmode_win = win;
 
2083
        
 
2084
        /* 
 
2085
         *   Shrink the window one line at the top, to make room for the mode
 
2086
         *   line.  Compensate by bumping the scrolling position forward a
 
2087
         *   line, to keep the lines that will remain visible in the rest of
 
2088
         *   the window at the same place on the screen.  
 
2089
         */
 
2090
        win->base.winy += 1;
 
2091
        win->base.ht -= 1;
 
2092
        win->base.scrolly += 1;
 
2093
 
 
2094
        /*
 
2095
         *   Temporarily add the command buffer to the scrollback buffer, so
 
2096
         *   that it shows up when we're scrolling.  To facilitate removing
 
2097
         *   the added text later, remember the current free pointer, last
 
2098
         *   line pointer, x document position.  
 
2099
         */
 
2100
        S_sbmode_orig_x = win->base.x;
 
2101
        S_sbmode_orig_txtfree = win->txtfree;
 
2102
        S_sbmode_orig_last_line = osgen_get_line_ptr(win, win->base.y);
 
2103
        ossaddsb_input(win, S_gets_buf, FALSE);
 
2104
 
 
2105
        /* remember the original scrolling position */
 
2106
        S_sbmode_orig_scrolly = win->base.scrolly;
 
2107
 
 
2108
        /* draw the mode line */
 
2109
        osgen_draw_sb_mode_line();
 
2110
    }
 
2111
 
 
2112
    /* presume we'll handle the event */
 
2113
    ret = TRUE;
 
2114
 
 
2115
    /* check the event */
 
2116
    switch(event_type)
 
2117
    {
 
2118
    case OS_EVT_KEY:
 
2119
        /* 
 
2120
         *   If it's a regular key, exit scrollback mode immediately, and
 
2121
         *   let the caller handle the event normally.  This allows the user
 
2122
         *   to resume typing even while scrollback is active; we'll simply
 
2123
         *   cancel the scrollback and resume editing with the event that
 
2124
         *   canceled the scrollback.  
 
2125
         */
 
2126
        c = event_info->key[0];
 
2127
        if ((unsigned char)c >= 32 || c == '\n' || c == '\r' || c == 8)
 
2128
        {
 
2129
            /* it's a regular key - terminate scrollback mode */
 
2130
            osgen_sb_mode_end();
 
2131
 
 
2132
            /* let the caller handle the event */
 
2133
            ret = FALSE;
 
2134
        }
 
2135
 
 
2136
        /* if it's the escape key, we're done */
 
2137
        if (c == 27)
 
2138
        {
 
2139
            osgen_sb_mode_end();
 
2140
            break;
 
2141
        }
 
2142
 
 
2143
        /* if it's not a special keystroke, we're done with it */
 
2144
        if (c != '\0')
 
2145
            break;
 
2146
 
 
2147
        /* it's a special keystroke - check the command ID */
 
2148
        switch(event_info->key[1])
 
2149
        {
 
2150
        case CMD_SCR:
 
2151
            /* if we're not just toggling in to scrollback mode, toggle out */
 
2152
            if (!just_started)
 
2153
                osgen_sb_mode_end();
 
2154
            break;
 
2155
            
 
2156
        case CMD_KILL:
 
2157
        case CMD_EOF:
 
2158
            /* it's a termination key - cancel scrollback mode */
 
2159
            osgen_sb_mode_end();
 
2160
            break;
 
2161
 
 
2162
        case CMD_UP:
 
2163
            /* scroll back a line */
 
2164
            osgen_scroll_win_back(win, 1);
 
2165
            break;
 
2166
 
 
2167
        case CMD_DOWN:
 
2168
            /* scroll forward a line */
 
2169
            osgen_scroll_win_fwd(win, 1);
 
2170
            break;
 
2171
            
 
2172
        case CMD_PGUP:
 
2173
            /* move back by one window height */
 
2174
            osgen_scroll_win_back(win, win->base.ht > 1
 
2175
                                  ? win->base.ht - 1 : 1);
 
2176
            break;
 
2177
            
 
2178
        case CMD_PGDN:
 
2179
            /* move forward by one window height */
 
2180
            osgen_scroll_win_fwd(win, win->base.ht > 1
 
2181
                                 ? win->base.ht - 1 : 1);
 
2182
            break;
 
2183
        }
 
2184
 
 
2185
        /* done with the key */
 
2186
        break;
 
2187
 
 
2188
    default:
 
2189
        /* 
 
2190
         *   ignore any other events we receive, but consider them processed
 
2191
         *   - even though we're ignoring the event, we're deliberately
 
2192
         *   ignoring the event as our way of processing it 
 
2193
         */
 
2194
        break;
 
2195
    }
 
2196
 
 
2197
    /* return the 'processed' indication to the caller */
 
2198
    return ret;
 
2199
}
 
2200
 
 
2201
/*
 
2202
 *   Display a line of text in the given ordinary text window 
 
2203
 */
 
2204
static void osgen_scrdisp_txt(osgen_txtwin_t *win, int x, int y, int len)
 
2205
{
 
2206
    int fg;
 
2207
    int bg;
 
2208
    int attr;
 
2209
    int oss_color;
 
2210
    char buf[64];
 
2211
    char *bufp;
 
2212
    char *p;
 
2213
    int scanx;
 
2214
 
 
2215
    /* start out in ordinary text mode */
 
2216
    fg = OSGEN_COLOR_TEXT;
 
2217
    bg = OSGEN_COLOR_TRANSPARENT;
 
2218
    attr = 0;
 
2219
    oss_color = ossgetcolor(fg, bg, attr, win->base.fillcolor);
 
2220
 
 
2221
    /* get a pointer to the start of the desired line */
 
2222
    p = osgen_get_line(win, y + win->base.scrolly);
 
2223
    if (p == 0)
 
2224
        return;
 
2225
 
 
2226
    /* get the window-relative x coordinate of the start of the line */
 
2227
    scanx = -win->base.scrollx;
 
2228
 
 
2229
    /* 
 
2230
     *   Scan the line.  We must scan from the start of the line, even if we
 
2231
     *   don't want to display from the start of the line, to make sure we
 
2232
     *   take into account any color and attribute settings stored in the
 
2233
     *   line.  
 
2234
     */
 
2235
    for (bufp = buf ; *p != '\0' && len != 0 ; p = ossadvsp(win, p))
 
2236
    {
 
2237
        /* check for special escape codes */
 
2238
        switch(*p)
 
2239
        {
 
2240
        case OSGEN_ATTR:
 
2241
            /* skip the escape byte */
 
2242
            p = ossadvsp(win, p);
 
2243
 
 
2244
            /* get the new attribute code */
 
2245
            attr = *p;
 
2246
 
 
2247
            /* set the new colors */
 
2248
            goto change_color;
 
2249
 
 
2250
        case OSGEN_COLOR:
 
2251
            /* skip the escape byte */
 
2252
            p = ossadvsp(win, p);
 
2253
 
 
2254
            /* get the foreground color and skip it */
 
2255
            fg = *p;
 
2256
            p = ossadvsp(win, p);
 
2257
 
 
2258
            /* get the background color */
 
2259
            bg = *p;
 
2260
            goto change_color;
 
2261
 
 
2262
        change_color:
 
2263
            /* flush the buffer */
 
2264
            if (bufp != buf)
 
2265
            {
 
2266
                /* display the contents of the buffer */
 
2267
                *bufp = '\0';
 
2268
                osgen_disp_trunc(&win->base, y, x, oss_color, buf);
 
2269
 
 
2270
                /* adjust the column position for the display */
 
2271
                x += bufp - buf;
 
2272
 
 
2273
                /* reset the buffer */
 
2274
                bufp = buf;
 
2275
            }
 
2276
 
 
2277
            /* translate the new text color */
 
2278
            oss_color = ossgetcolor(fg, bg, attr, win->base.fillcolor);
 
2279
            break;
 
2280
 
 
2281
        default:
 
2282
            /* if the buffer is full, flush it */
 
2283
            if (bufp == buf + sizeof(buf) - 1)
 
2284
            {
 
2285
                /* display the buffer */
 
2286
                *bufp = '\0';
 
2287
                osgen_disp_trunc(&win->base, y, x, oss_color, buf);
 
2288
 
 
2289
                /* adjust the column position for the display */
 
2290
                x += bufp - buf;
 
2291
 
 
2292
                /* empty the buffer */
 
2293
                bufp = buf;
 
2294
            }
 
2295
 
 
2296
            /* 
 
2297
             *   if we've reached the starting x coordinate, add this
 
2298
             *   character to the buffer; if we haven't, we can ignore the
 
2299
             *   character, since in that case we're just scanning for
 
2300
             *   escape codes in the line before the part we want to display 
 
2301
             */
 
2302
            if (scanx >= x)
 
2303
            {
 
2304
                /* add it to the buffer */
 
2305
                *bufp++ = *p;
 
2306
 
 
2307
                /* count it against the length remaining to be displayed */
 
2308
                --len;
 
2309
            }
 
2310
 
 
2311
            /* adjust the x coordinate of our scan */
 
2312
            ++scanx;
 
2313
 
 
2314
            /* done */
 
2315
            break;
 
2316
        }
 
2317
    }
 
2318
 
 
2319
    /* display the last portion of the line if anything's left */
 
2320
    if (bufp != buf)
 
2321
    {
 
2322
        /* null-terminate at the current point */
 
2323
        *bufp = '\0';
 
2324
 
 
2325
        /* display it */
 
2326
        osgen_disp_trunc(&win->base, y, x, oss_color, buf);
 
2327
    }
 
2328
}
 
2329
 
 
2330
 
 
2331
/* ------------------------------------------------------------------------ */
 
2332
/*
 
2333
 *   Special routines for Text Grid windows
 
2334
 */
 
2335
 
 
2336
/*
 
2337
 *   Clear the text and color arrays for a section of a grid window.  Sets
 
2338
 *   each cleared character's text to a space, and sets the color to
 
2339
 *   text/transparent.  
 
2340
 */
 
2341
static void osgen_gridwin_clear_ptr(char *txtp, osgen_charcolor_t *colorp,
 
2342
                                    size_t len)
 
2343
{
 
2344
    /* loop through the grid from the starting offset */
 
2345
    for ( ; len != 0 ; --len, ++txtp, ++colorp)
 
2346
    {
 
2347
        /* set this character's text to a space */
 
2348
        *txtp = ' ';
 
2349
 
 
2350
        /* set this character's color to text/transparent */
 
2351
        colorp->fg = OSGEN_COLOR_TEXT;
 
2352
        colorp->bg = OSGEN_COLOR_TRANSPARENT;
 
2353
    }
 
2354
}
 
2355
 
 
2356
/*
 
2357
 *   Clear the text and color arrays for a section of a grid window.  Starts
 
2358
 *   at the given character offset in the grid arrays, and clears for the
 
2359
 *   given number of character positions.  Sets each cleared character's
 
2360
 *   text to a space, and sets the color to text/transparent.  
 
2361
 */
 
2362
static void osgen_gridwin_clear(osgen_gridwin_t *win, size_t ofs, size_t len)
 
2363
{
 
2364
    /* clear our array in the selected areas */
 
2365
    osgen_gridwin_clear_ptr(win->grid_txt + ofs, win->grid_color + ofs, len);
 
2366
}
 
2367
 
 
2368
 
 
2369
/*
 
2370
 *   Resize a text grid window.  This must be called whenever the on-screen
 
2371
 *   size of the window is changed, or we need to write outside the current
 
2372
 *   bounds of the array, so that we can expand our internal size allocation
 
2373
 *   if the window is now bigger than our internal text/color arrays.  
 
2374
 */
 
2375
static void osgen_gridwin_resize(osgen_gridwin_t *win,
 
2376
                                 size_t new_wid, size_t new_ht)
 
2377
{
 
2378
    /* if the window's size is now bigger than the grid, expand the grid */
 
2379
    if (new_wid > win->grid_wid || new_ht > win->grid_ht)
 
2380
    {
 
2381
        char *new_txt;
 
2382
        size_t ofs;
 
2383
        osgen_charcolor_t *new_color;
 
2384
        char *tsrc, *tdst;
 
2385
        osgen_charcolor_t *csrc, *cdst;
 
2386
        size_t y;
 
2387
 
 
2388
        /*  
 
2389
         *   use the larger of the old size and the new size, so that the
 
2390
         *   window only expands (this somewhat simplifies copying the old
 
2391
         *   to the new) 
 
2392
         */
 
2393
        if (win->grid_wid > new_wid)
 
2394
            new_wid = win->grid_wid;
 
2395
        if (win->grid_ht > new_ht)
 
2396
            new_ht = win->grid_ht;
 
2397
 
 
2398
        /* allocate the new arrays */
 
2399
        new_txt = (char *)osmalloc(new_wid * new_ht);
 
2400
        new_color = (osgen_charcolor_t *)osmalloc(
 
2401
            new_wid * new_ht * sizeof(new_color[0]));
 
2402
 
 
2403
        /* copy the old grid to the new grid */
 
2404
        tsrc = win->grid_txt;
 
2405
        csrc = win->grid_color;
 
2406
        tdst = new_txt;
 
2407
        cdst = new_color;
 
2408
        for (y = 0, ofs = 0 ; y < win->grid_ht ; ++y)
 
2409
        {
 
2410
            /* copy the old text and color data */
 
2411
            memcpy(tdst, tsrc, win->grid_wid);
 
2412
            memcpy(cdst, csrc, win->grid_wid * sizeof(cdst[0]));
 
2413
 
 
2414
            /* clear the rest of the line if expanding the width */
 
2415
            osgen_gridwin_clear_ptr(tdst + win->grid_wid,
 
2416
                                    cdst + win->grid_wid,
 
2417
                                    new_wid - win->grid_wid);
 
2418
 
 
2419
            /* advance the pointers */
 
2420
            tsrc += win->grid_wid;
 
2421
            csrc += win->grid_wid;
 
2422
            tdst += new_wid;
 
2423
            cdst += new_wid;
 
2424
        }
 
2425
 
 
2426
        /* clear all remaining lines */
 
2427
        for ( ; y < new_ht ; ++y, tdst += new_wid, cdst += new_wid)
 
2428
            osgen_gridwin_clear_ptr(tdst, cdst, new_wid);
 
2429
 
 
2430
        /* delete the old buffers */
 
2431
        osfree(win->grid_txt);
 
2432
        osfree(win->grid_color);
 
2433
 
 
2434
        /* set the new buffers and sizes */
 
2435
        win->grid_txt = new_txt;
 
2436
        win->grid_color = new_color;
 
2437
        win->grid_wid = new_wid;
 
2438
        win->grid_ht = new_ht;
 
2439
    }
 
2440
}
 
2441
 
 
2442
/*
 
2443
 *   Display a line of text in the given grid window 
 
2444
 */
 
2445
static void osgen_scrdisp_grid(osgen_gridwin_t *win, int x, int y, int len)
 
2446
{
 
2447
    char *txtp;
 
2448
    osgen_charcolor_t *colorp;
 
2449
    size_t ofs;
 
2450
    char buf[64];
 
2451
    char *dst;
 
2452
    char fg, bg;
 
2453
    int oss_color;
 
2454
 
 
2455
    /* 
 
2456
     *   calculate the offset into our arrays: get the document coordinates
 
2457
     *   (by adjusting for the scrolling offset), multiply the document row
 
2458
     *   number by the row width, and add the document column number 
 
2459
     */
 
2460
    ofs = ((y - win->base.scrolly) * win->grid_wid) + (x - win->base.scrollx);
 
2461
 
 
2462
    /* start at the calculated offset */
 
2463
    txtp = win->grid_txt + ofs;
 
2464
    colorp = win->grid_color + ofs;
 
2465
 
 
2466
    /* start with the first character's color */
 
2467
    fg = colorp->fg;
 
2468
    bg = colorp->bg;
 
2469
 
 
2470
    /* calculate the starting oss-level color */
 
2471
    oss_color = ossgetcolor(fg, bg, 0, win->base.fillcolor);
 
2472
 
 
2473
    /* 
 
2474
     *   scan the text, flushing when we fill up the buffer or encounter a
 
2475
     *   new text color 
 
2476
     */
 
2477
    for (dst = buf ; ; --len, ++txtp, ++colorp)
 
2478
    {
 
2479
        /* 
 
2480
         *   if we have a color change, or we've exhausted the requested
 
2481
         *   length, or the buffer is full, display what we have in the
 
2482
         *   buffer so far 
 
2483
         */
 
2484
        if (len == 0
 
2485
            || dst == buf + sizeof(buf) - 1
 
2486
            || colorp->fg != fg || colorp->bg != bg)
 
2487
        {
 
2488
            /* null-terminate the buffer and display it */
 
2489
            *dst = '\0';
 
2490
            osgen_disp_trunc(&win->base, y, x, oss_color, buf);
 
2491
 
 
2492
            /* count the output column change */
 
2493
            x += dst - buf;
 
2494
 
 
2495
            /* reset the buffer write pointer */
 
2496
            dst = buf;
 
2497
 
 
2498
            /* if we have a color change, calculate the new color */
 
2499
            if (colorp->fg != fg || colorp->bg != bg)
 
2500
            {
 
2501
                /* remember the new color */
 
2502
                fg = colorp->fg;
 
2503
                bg = colorp->bg;
 
2504
 
 
2505
                /* calculate the new oss-level color */
 
2506
                oss_color = ossgetcolor(fg, bg, 0, win->base.fillcolor);
 
2507
            }
 
2508
        }
 
2509
 
 
2510
        /* if we've exhausted the request, we're done */
 
2511
        if (len == 0)
 
2512
            break;
 
2513
 
 
2514
        /* add this character from the text grid to our output buffer */
 
2515
        *dst++ = *txtp;
 
2516
    }
 
2517
}
 
2518
 
 
2519
/*
 
2520
 *   Write text into a grid window
 
2521
 */
 
2522
static void osgen_gridwin_write(osgen_gridwin_t *win,
 
2523
                                const char *txt, size_t len)
 
2524
{
 
2525
    const char *p;
 
2526
    size_t rem;
 
2527
    int xmax, ymax;
 
2528
    int x, y;
 
2529
    int startx;
 
2530
    int winx;
 
2531
    size_t ofs;
 
2532
    char *tdst;
 
2533
    osgen_charcolor_t *cdst;
 
2534
 
 
2535
    /*
 
2536
     *   First, scan the text to check for writing beyond the end of the
 
2537
     *   current text array.  If we write anything beyond the bounds of the
 
2538
     *   current array, we'll need to expand the array accordingly. 
 
2539
     */
 
2540
    xmax = x = win->base.x;
 
2541
    ymax = y = win->base.y;
 
2542
    for (p = txt, rem = len ; rem != 0 ; ++p, --rem)
 
2543
    {
 
2544
        /* check what we have */
 
2545
        switch(*p)
 
2546
        {
 
2547
        case '\n':
 
2548
            /* move to the start of the next line */
 
2549
            x = 0;
 
2550
            ++y;
 
2551
            break;
 
2552
 
 
2553
        case '\r':
 
2554
            /* move to the start of the current line */
 
2555
            x = 0;
 
2556
            break;
 
2557
 
 
2558
        default:
 
2559
            /* 
 
2560
             *   everything else takes up a character cell; if this is the
 
2561
             *   highest x/y position so far, note it 
 
2562
             */
 
2563
            if (y > ymax)
 
2564
                ymax = y;
 
2565
            if (x > xmax)
 
2566
                xmax = x;
 
2567
 
 
2568
            /* move the cursor right */
 
2569
            ++x;
 
2570
            break;
 
2571
        }
 
2572
    }
 
2573
 
 
2574
    /* expand our text array if we're writing outside the current bounds */
 
2575
    if (xmax >= (int)win->grid_wid || ymax >= (int)win->grid_ht)
 
2576
        osgen_gridwin_resize(win, xmax + 1, ymax + 1);
 
2577
 
 
2578
    /* start at the current window coordinates */
 
2579
    x = win->base.x;
 
2580
    y = win->base.y;
 
2581
 
 
2582
    /* calculate the starting offset in the array for writing */
 
2583
    ofs = y * win->grid_wid + x;
 
2584
    tdst = win->grid_txt + ofs;
 
2585
    cdst = win->grid_color + ofs;
 
2586
 
 
2587
    /* note the new maximum write positions */
 
2588
    if (xmax > win->base.xmax)
 
2589
        win->base.xmax = xmax;
 
2590
    if (ymax > win->base.ymax)
 
2591
        win->base.ymax = ymax;
 
2592
 
 
2593
    /* start at the current column */
 
2594
    startx = x;
 
2595
    winx = x - win->base.scrollx;
 
2596
 
 
2597
    /* now scan the text again, writing it to the grid and displaying it */
 
2598
    for (p = txt, rem = len ; ; ++p, --rem)
 
2599
    {
 
2600
        /* 
 
2601
         *   if we're at a newline or the end of the text to display,
 
2602
         *   display the section of the current line that we just built 
 
2603
         */
 
2604
        if (rem == 0 || *p == '\n' || *p == '\r')
 
2605
        {
 
2606
            /* if we're not in deferred redraw mode, draw this section */
 
2607
            if (!S_deferred_redraw
 
2608
                && !(win->base.flags & OSGEN_DEFER_REDRAW)
 
2609
                && x != startx)
 
2610
                osgen_scrdisp(&win->base, winx, y, x - startx);
 
2611
 
 
2612
            /* if we're out of text to display, we're done */
 
2613
            if (rem == 0)
 
2614
                break;
 
2615
 
 
2616
            /* apply any newline */
 
2617
            if (*p == '\n')
 
2618
            {
 
2619
                /* move to the next line */
 
2620
                x = 0;
 
2621
                ++y;
 
2622
            }
 
2623
            else if (*p == '\r')
 
2624
            {
 
2625
                /* move to the start of the current line */
 
2626
                x = 0;
 
2627
            }
 
2628
 
 
2629
            /* note the starting coordinates of the next chunk */
 
2630
            startx = x;
 
2631
            winx = x - win->base.scrollx;
 
2632
 
 
2633
            /* calculate the new array output offset */
 
2634
            ofs = y * win->grid_wid + x;
 
2635
            tdst = win->grid_txt + ofs;
 
2636
            cdst = win->grid_color + ofs;
 
2637
        }
 
2638
        else
 
2639
        {
 
2640
            /* add this character to our array */
 
2641
            *tdst = *p;
 
2642
            cdst->fg = win->base.txtfg;
 
2643
            cdst->bg = win->base.txtbg;
 
2644
 
 
2645
            /* ajust the output pointers */
 
2646
            ++tdst;
 
2647
            ++cdst;
 
2648
 
 
2649
            /* adjust the column counter */
 
2650
            ++x;
 
2651
        }
 
2652
    }
 
2653
 
 
2654
    /* set the new output position in the window */
 
2655
    win->base.x = x;
 
2656
    win->base.y = y;
 
2657
}
 
2658
 
 
2659
 
 
2660
/* ------------------------------------------------------------------------ */
 
2661
/*
 
2662
 *   Generic banner window routines 
 
2663
 */
 
2664
 
 
2665
/*
 
2666
 *   Display a window's text from the given starting position for the given
 
2667
 *   number of characters (counting only displayed characters; escape
 
2668
 *   sequences don't count).  The starting position is given in
 
2669
 *   window-relative coordinates.  
 
2670
 */
 
2671
static void osgen_scrdisp(osgen_win_t *win, int x, int y, int len)
 
2672
{
 
2673
    /* 
 
2674
     *   if the text is entirely outside the window's display area, there's
 
2675
     *   nothing to display, so ignore the request 
 
2676
     */
 
2677
    if (y < 0 || (size_t)y >= win->ht
 
2678
        || x + len <= 0 || (size_t)x >= win->wid)
 
2679
        return;
 
2680
 
 
2681
    /* draw according to window type */
 
2682
    switch(win->win_type)
 
2683
    {
 
2684
    case OS_BANNER_TYPE_TEXT:
 
2685
        osgen_scrdisp_txt((osgen_txtwin_t *)win, x, y, len);
 
2686
        break;
 
2687
 
 
2688
    case OS_BANNER_TYPE_TEXTGRID:
 
2689
        osgen_scrdisp_grid((osgen_gridwin_t *)win, x, y, len);
 
2690
        break;
 
2691
    }
 
2692
}
 
2693
 
 
2694
 
 
2695
/* 
 
2696
 *   redraw a window 
 
2697
 */
 
2698
static void osgen_redraw_win(osgen_win_t *win)
 
2699
{
 
2700
    size_t y;
 
2701
 
 
2702
    /* clear the window's area on the screen */
 
2703
    ossclr(win->winy, win->winx,
 
2704
           win->winy + win->ht - 1, win->winx + win->wid - 1,
 
2705
           win->oss_fillcolor);
 
2706
 
 
2707
    /* display each line in the window */
 
2708
    for (y = 0 ; y < win->ht ; ++y)
 
2709
        osgen_scrdisp(win, 0, y, win->wid);
 
2710
}
 
2711
 
 
2712
/* redraw the entire screen */
 
2713
void os_redraw(void)
 
2714
{
 
2715
    /* 
 
2716
     *   force a redraw of the entire screen by setting the global
 
2717
     *   pending-redraw flag 
 
2718
     */
 
2719
    S_deferred_redraw = TRUE;
 
2720
 
 
2721
    /* go redraw it */
 
2722
    osssb_redraw_if_needed();
 
2723
    
 
2724
}
 
2725
 
 
2726
/* redraw a window, if it needs redrawing */
 
2727
static void osgen_redraw_win_if_needed(int global_deferred, osgen_win_t *win)
 
2728
{
 
2729
    osgen_win_t *chi;
 
2730
 
 
2731
    /* 
 
2732
     *   if this window needs redrawing, or we have a global deferred redraw,
 
2733
     *   redraw the window 
 
2734
     */
 
2735
    if (global_deferred || (win->flags & OSGEN_DEFER_REDRAW) != 0)
 
2736
    {
 
2737
        /* clear the window-specific deferred-redraw flag */
 
2738
        win->flags &= ~OSGEN_DEFER_REDRAW;
 
2739
 
 
2740
        /* redraw the window */
 
2741
        osgen_redraw_win(win);
 
2742
    }
 
2743
 
 
2744
    /* redraw this window's children if necessary */
 
2745
    for (chi = win->first_child ; chi != 0 ; chi = chi->nxt)
 
2746
        osgen_redraw_win_if_needed(global_deferred, chi);
 
2747
}
 
2748
 
 
2749
/* redraw the screen if necessary */
 
2750
void osssb_redraw_if_needed()
 
2751
{
 
2752
    int global_deferred = S_deferred_redraw;
 
2753
 
 
2754
    /* we're explicitly redrawing, so cancel any pending deferred redraw */
 
2755
    S_deferred_redraw = FALSE;
 
2756
 
 
2757
    /* redraw the root window, which will redraw its children */
 
2758
    if (S_main_win != 0)
 
2759
        osgen_redraw_win_if_needed(global_deferred, &S_main_win->base);
 
2760
 
 
2761
    /* if the redraw is global, redraw other global features */
 
2762
    if (global_deferred)
 
2763
    {
 
2764
        /* redraw the scrollback mode line, if appropriate */
 
2765
        osgen_draw_sb_mode_line();
 
2766
 
 
2767
        /* redraw any command line under construction */
 
2768
        osgen_gets_redraw_cmdline();
 
2769
    }
 
2770
}
 
2771
 
 
2772
/* move the cursor to the default position */
 
2773
void osssb_cursor_to_default_pos(void)
 
2774
{
 
2775
    osgen_win_t *win;
 
2776
 
 
2777
    /* if we're in plain mode, ignore it */
 
2778
    if (os_f_plain)
 
2779
        return;
 
2780
 
 
2781
    /* if we're using a special cursor position, do nothing */
 
2782
    if (S_sbmode_win != 0)
 
2783
    {
 
2784
        /* locate at the scrollback mode line */
 
2785
        ossloc(S_sbmode_win->base.winy - 1, S_sbmode_win->base.winx);
 
2786
    }
 
2787
    else if (S_special_cursor_pos)
 
2788
    {
 
2789
        /* locate at the special cursor position */
 
2790
        ossloc(S_special_cursor_y, S_special_cursor_x);
 
2791
    }
 
2792
    else
 
2793
    {
 
2794
        /* 
 
2795
         *   if we have a default window, put the cursor at the last text
 
2796
         *   position in the default window 
 
2797
         */
 
2798
        if ((win = &S_default_win->base) != 0)
 
2799
            ossloc(win->winy + win->y - win->scrolly,
 
2800
                   win->winx + win->x - win->scrollx);
 
2801
    }
 
2802
}
 
2803
 
 
2804
/*
 
2805
 *   Lay out the given window 
 
2806
 */
 
2807
static void oss_lay_out_window(osgen_win_t *win)
 
2808
{
 
2809
    osgen_win_t *chi;
 
2810
 
 
2811
    /* 
 
2812
     *   if we have a parent, take space from our parent window; otherwise,
 
2813
     *   assume that the caller has already laid out our main area, in which
 
2814
     *   case we just have to adjust for our children 
 
2815
     */
 
2816
    if (win->parent != 0)
 
2817
    {
 
2818
        int siz;
 
2819
        int base_size;
 
2820
        int x, y, wid, ht;
 
2821
 
 
2822
        /* 
 
2823
         *   our size and area will be taken from our parent's, so get the
 
2824
         *   parent window's current area 
 
2825
         */
 
2826
        x = win->parent->winx;
 
2827
        y = win->parent->winy;
 
2828
        wid = win->parent->wid;
 
2829
        ht = win->parent->ht;
 
2830
 
 
2831
        /* get the window's size in character cells */
 
2832
        switch(win->size_type)
 
2833
        {
 
2834
        case OS_BANNER_SIZE_ABS:
 
2835
            /* the size is given in character cells */
 
2836
            siz = win->size;
 
2837
            break;
 
2838
 
 
2839
        case OS_BANNER_SIZE_PCT:
 
2840
            /* 
 
2841
             *   the size is given as a percentage of the parent's size - get
 
2842
             *   the appropriate dimension from the parent's size 
 
2843
             */
 
2844
            base_size = (win->alignment == OS_BANNER_ALIGN_LEFT
 
2845
                         || win->alignment == OS_BANNER_ALIGN_RIGHT
 
2846
                         ? wid : ht);
 
2847
 
 
2848
            /* calculate the percentage of the full size */
 
2849
            siz = (win->size * base_size) / 100;
 
2850
            break;
 
2851
        }
 
2852
 
 
2853
        /* allocate space to the window according to its alignment */
 
2854
        switch(win->alignment)
 
2855
        {
 
2856
        case OS_BANNER_ALIGN_TOP:
 
2857
            /* 
 
2858
             *   assign the window the full width of the parent, and give it
 
2859
             *   the requested height, up to the available height 
 
2860
             */
 
2861
            win->wid = wid;
 
2862
            win->winx = x;
 
2863
            win->ht = (siz <= ht ? siz : ht);
 
2864
            win->winy = y;
 
2865
 
 
2866
            /* take the window's space away from the top of the parent */
 
2867
            ht -= win->ht;
 
2868
            y += win->ht;
 
2869
            break;
 
2870
 
 
2871
        case OS_BANNER_ALIGN_BOTTOM:
 
2872
            /* give the window space at the bottom of the parent window */
 
2873
            win->wid = wid;
 
2874
            win->winx = x;
 
2875
            win->ht = (siz <= ht ? siz : ht);
 
2876
            win->winy = y + ht - win->ht;
 
2877
 
 
2878
            /* deduct the window from the parent area */
 
2879
            ht -= win->ht;
 
2880
            break;
 
2881
 
 
2882
        case OS_BANNER_ALIGN_LEFT:
 
2883
            /* give the window space at the left of the parent area */
 
2884
            win->wid = (siz <= wid ? siz : wid);
 
2885
            win->winx = x;
 
2886
            win->ht = ht;
 
2887
            win->winy = y;
 
2888
 
 
2889
            /* deduct the window from the parent area */
 
2890
            wid -= win->wid;
 
2891
            x += win->wid;
 
2892
            break;
 
2893
 
 
2894
        case OS_BANNER_ALIGN_RIGHT:
 
2895
            /* give the window space at the right of the remaining area */
 
2896
            win->wid = (siz <= wid ? siz : wid);
 
2897
            win->winx = x + wid - win->wid;
 
2898
            win->ht = ht;
 
2899
            win->winy = y;
 
2900
 
 
2901
            /* deduct the window from the parent area */
 
2902
            wid -= win->wid;
 
2903
            break;
 
2904
        }
 
2905
 
 
2906
        /* adjust our parent's area for the removal of our area */
 
2907
        win->parent->winx = x;
 
2908
        win->parent->winy = y;
 
2909
        win->parent->wid = wid;
 
2910
        win->parent->ht = ht;
 
2911
    }
 
2912
 
 
2913
    /* lay out our children */
 
2914
    for (chi = win->first_child ; chi != 0 ; chi = chi->nxt)
 
2915
        oss_lay_out_window(chi);
 
2916
 
 
2917
    /* make any necessary adjustments */
 
2918
    switch(win->win_type)
 
2919
    {
 
2920
    case OS_BANNER_TYPE_TEXT:
 
2921
        /*
 
2922
         *   If this is the current active scrollback-mode window, we must
 
2923
         *   make the same adjustment that we make on entering scrollback
 
2924
         *   mode: shrink the window one line from the top, to make room for
 
2925
         *   the scrollback-mode status line.  
 
2926
         */
 
2927
        if (win == &S_sbmode_win->base)
 
2928
        {
 
2929
            /* it's the scrollback window - take out the mode line */
 
2930
            win->winy += 1;
 
2931
            win->ht -= 1;
 
2932
        }
 
2933
        break;
 
2934
 
 
2935
    case OS_BANNER_TYPE_TEXTGRID:
 
2936
        /* make sure the window's text/color buffers are large enough */
 
2937
        osgen_gridwin_resize((osgen_gridwin_t *)win, win->wid, win->ht);
 
2938
        break;
 
2939
    }
 
2940
}
 
2941
 
 
2942
/*
 
2943
 *   Recalculate the window layout and redraw the screen 
 
2944
 */
 
2945
static void osgen_recalc_layout()
 
2946
{
 
2947
    /* start at the root window */
 
2948
    if (S_main_win != 0)
 
2949
    {
 
2950
        int ht;
 
2951
 
 
2952
        /* start by giving the entire screen to the main window */
 
2953
        S_main_win->base.winx = 0;
 
2954
        S_main_win->base.winy = 0;
 
2955
        S_main_win->base.wid = G_oss_screen_width;
 
2956
        S_main_win->base.ht = G_oss_screen_height;
 
2957
 
 
2958
        /* lay out the main window and its children */
 
2959
        oss_lay_out_window(&S_main_win->base);
 
2960
 
 
2961
        /* recalculate the main window's page size */
 
2962
        G_os_linewidth = S_main_win->base.wid;
 
2963
        ht = S_main_win->base.ht;
 
2964
        G_os_pagelength = (ht > 2 ? ht - 2 : ht > 1 ? ht - 1 : ht);
 
2965
    }
 
2966
 
 
2967
    /* schedule a redraw of the entire screen */
 
2968
    S_deferred_redraw = TRUE;
 
2969
}
 
2970
 
 
2971
/*
 
2972
 *   Receive notification that the screen was resized.  We'll recalculate
 
2973
 *   the banner window layout.  
 
2974
 */
 
2975
void osssb_on_resize_screen()
 
2976
{
 
2977
    /* recalculate the window layout */
 
2978
    osgen_recalc_layout();
 
2979
 
 
2980
    /* immediately update the screen */
 
2981
    osssb_redraw_if_needed();
 
2982
 
 
2983
    /* set the cursor back to the default position */
 
2984
    osssb_cursor_to_default_pos();
 
2985
}
 
2986
 
 
2987
# else /* USE_SCROLLBACK */
 
2988
/* 
 
2989
 *   for the non-scrollback version, add-to-scrollback is just a dummy
 
2990
 *   function: if we're not saving scrollback, we obviously have no need to
 
2991
 *   save any information added to the buffer 
 
2992
 */
 
2993
static void ossaddsb(struct osgen_win_t *win, char *p, size_t len, int draw)
 
2994
{
 
2995
    /* do nothing */
 
2996
}
 
2997
 
 
2998
/* 
 
2999
 *   for the non-scrollback version, there's nothing we need to do on
 
3000
 *   resizing the screen, as we don't do anything fancy with the layout 
 
3001
 */
 
3002
void osssb_on_resize_screen()
 
3003
{
 
3004
    /* do nothing */
 
3005
}
 
3006
 
 
3007
# endif /* USE_SCROLLBACK */
 
3008
 
 
3009
/* ------------------------------------------------------------------------ */
 
3010
 
 
3011
#ifdef USE_STATLINE
 
3012
 
 
3013
/* current statusline score-area string */
 
3014
static osfar_t char S_scorebuf[135];
 
3015
 
 
3016
/* statusline left-string reset point */
 
3017
static osfar_t char *S_stat_reset_free;
 
3018
static osfar_t int S_stat_reset_x;
 
3019
 
 
3020
/* update the right portion of the statusline */
 
3021
static void osgen_update_stat_right()
 
3022
{
 
3023
    osgen_txtwin_t *win;
 
3024
    int wid_rem;
 
3025
    size_t char_len;
 
3026
    
 
3027
    /* 
 
3028
     *   if there's no statusline window, or no reset point, we can't do
 
3029
     *   anything right now 
 
3030
     */
 
3031
    if ((win = S_status_win) == 0 || S_stat_reset_free == 0)
 
3032
        return;
 
3033
 
 
3034
    /* 
 
3035
     *   set the statusline position back to where it was when we finished
 
3036
     *   with the statusline itself
 
3037
     */
 
3038
    win->txtfree = S_stat_reset_free;
 
3039
    win->base.x = S_stat_reset_x;
 
3040
 
 
3041
    /* if there's no space, don't draw anything */
 
3042
    if (win->base.x > (int)win->base.wid)
 
3043
        return;
 
3044
 
 
3045
    /* figure out how much space in the window we have */
 
3046
    wid_rem = win->base.wid - win->base.x;
 
3047
 
 
3048
    /* figure out how much we're adding */
 
3049
    char_len = strlen(S_scorebuf);
 
3050
 
 
3051
    /* 
 
3052
     *   if we're adding more than we have room for, add a space and then
 
3053
     *   add the buffer contents 
 
3054
     */
 
3055
    if ((int)char_len + 1 >= wid_rem)
 
3056
    {
 
3057
        /* add a space */
 
3058
        ossaddsb(win, " ", 1, TRUE);
 
3059
 
 
3060
        /* add the right-half string */
 
3061
        ossaddsb(win, S_scorebuf, strlen(S_scorebuf), TRUE);
 
3062
    }
 
3063
    else
 
3064
    {
 
3065
        /* add spaces to right-align the scorebuf text */
 
3066
        while (wid_rem > (int)char_len + 1)
 
3067
        {
 
3068
            char buf[64];
 
3069
            size_t cur;
 
3070
            
 
3071
            /* add the remaining spaces, up to a buffer-full */
 
3072
            cur = wid_rem - char_len - 1;
 
3073
            if (cur > sizeof(buf) - 1)
 
3074
                cur = sizeof(buf) - 1;
 
3075
 
 
3076
            /* make a buffer-full of spaces */
 
3077
            memset(buf, ' ', cur);
 
3078
 
 
3079
            /* display it */
 
3080
            ossaddsb(win, buf, cur, TRUE);
 
3081
 
 
3082
            /* deduct the amount we displayed from the width remaining */
 
3083
            wid_rem -= cur;
 
3084
        }
 
3085
 
 
3086
        /* add the right-half string */
 
3087
        ossaddsb(win, S_scorebuf, strlen(S_scorebuf), TRUE);
 
3088
    }
 
3089
}
 
3090
 
 
3091
/*
 
3092
 *   Set the status mode.  In status mode 0, text displayed via os_print and
 
3093
 *   the like will display to the main window; in mode 1, text displayed will
 
3094
 *   go to the default status line window, until we reach a newline, at which
 
3095
 *   point we'll switch to status mode 2 and text will go nowhere.  
 
3096
 */
 
3097
void os_status(int stat)
 
3098
{
 
3099
    /* if we're in 'plain' mode, suppress all statusline output */
 
3100
    if (os_f_plain)
 
3101
    {
 
3102
        /* check the requested mode */
 
3103
        switch(stat)
 
3104
        {
 
3105
        case 0:
 
3106
            /* switching to main text mode */
 
3107
            status_mode = 0;
 
3108
            break;
 
3109
 
 
3110
        case 1:
 
3111
            /* switching to statusline mode - suppress all output */
 
3112
            status_mode = 2;
 
3113
            break;
 
3114
 
 
3115
        case 2:
 
3116
            /* suppress mode */
 
3117
            status_mode = 2;
 
3118
            break;
 
3119
        }
 
3120
 
 
3121
        /* done */
 
3122
        return;
 
3123
    }
 
3124
 
 
3125
    /* if there's no statusline window, create one */
 
3126
    if (S_status_win == 0)
 
3127
    {
 
3128
        /* create the statusline window as a child of the main window */
 
3129
        S_status_win = osgen_create_txtwin(OS_BANNER_FIRST, 0, S_main_win,
 
3130
                                           512, 1);
 
3131
 
 
3132
        /* if that succeeded, set up the window */
 
3133
        if (S_status_win != 0)
 
3134
        {
 
3135
            /* set up the statusline window at the top of the screen */
 
3136
            S_status_win->base.alignment = OS_BANNER_ALIGN_TOP;
 
3137
            S_status_win->base.size = 1;
 
3138
            S_status_win->base.size_type = OS_BANNER_SIZE_ABS;
 
3139
 
 
3140
            /* use the default statusline color in this window */
 
3141
            S_status_win->base.txtfg = OSGEN_COLOR_STATUSLINE;
 
3142
            S_status_win->base.txtbg = OSGEN_COLOR_TRANSPARENT;
 
3143
            S_status_win->base.fillcolor = OSGEN_COLOR_STATUSBG;
 
3144
 
 
3145
            /* cache the oss translations of the colors */
 
3146
            S_status_win->base.oss_fillcolor =
 
3147
                ossgetcolor(OSGEN_COLOR_STATUSLINE, OSGEN_COLOR_STATUSBG,
 
3148
                            0, 0);
 
3149
 
 
3150
            /* recalculate the window layout */
 
3151
            osgen_recalc_layout();
 
3152
        }
 
3153
    }
 
3154
 
 
3155
    /* if entering status mode 1, clear the statusline */
 
3156
    if (stat == 1 && status_mode != 1 && S_status_win != 0)
 
3157
    {
 
3158
        /* switch the default window to the status line */
 
3159
        S_default_win = S_status_win;
 
3160
 
 
3161
        /* clear the statusline window */
 
3162
        osgen_clear_win(&S_status_win->base);
 
3163
 
 
3164
        /* forget the score reset point */
 
3165
        S_stat_reset_free = 0;
 
3166
    }
 
3167
 
 
3168
    /* if we're leaving status mode 1, finish the statusline */
 
3169
    if (status_mode == 1 && stat != 1 && S_status_win != 0)
 
3170
    {
 
3171
        /* 
 
3172
         *   remember the current statusline window settings as the "reset"
 
3173
         *   point - this is where we reset the buffer contents whenever we
 
3174
         *   want to add the right-half string 
 
3175
         */
 
3176
        S_stat_reset_free = S_status_win->txtfree;
 
3177
        S_stat_reset_x = S_status_win->base.x;
 
3178
 
 
3179
        /* update the right-half string */
 
3180
        osgen_update_stat_right();
 
3181
    }
 
3182
 
 
3183
    /* switch to the new mode */
 
3184
    status_mode = stat;
 
3185
 
 
3186
    /* check the mode */
 
3187
    if (status_mode == 0)
 
3188
    {
 
3189
        /* switching to the main window */
 
3190
        S_default_win = S_main_win;
 
3191
    }
 
3192
    else if (status_mode == 2)
 
3193
    {
 
3194
        /* 
 
3195
         *   entering post-status mode - ignore everything, so set the
 
3196
         *   default window to null 
 
3197
         */
 
3198
        S_default_win = 0;
 
3199
    }
 
3200
}
 
3201
 
 
3202
int os_get_status()
 
3203
{
 
3204
    return status_mode;
 
3205
}
 
3206
 
 
3207
/* Set score to a string value provided by the caller */
 
3208
void os_strsc(const char *p)
 
3209
{
 
3210
    size_t copy_len;
 
3211
 
 
3212
    /* copy the score, if a value was given */
 
3213
    if (p != 0)
 
3214
    {
 
3215
        /* limit the copying length to our buffer size */
 
3216
        copy_len = strlen(p);
 
3217
        if (copy_len > sizeof(S_scorebuf) - 1)
 
3218
            copy_len = sizeof(S_scorebuf) - 1;
 
3219
        
 
3220
        /* copy the text and null-terminate it */
 
3221
        memcpy(S_scorebuf, p, copy_len);
 
3222
        S_scorebuf[copy_len] = '\0';
 
3223
    }
 
3224
 
 
3225
    /* update the statusline window */
 
3226
    osgen_update_stat_right();
 
3227
}
 
3228
 
 
3229
/*
 
3230
 *   Set the score.  If cur == -1, the LAST score set with a non-(-1) cur is
 
3231
 *   displayed; this is used to refresh the status line without providing a
 
3232
 *   new score (for example, after exiting scrollback mode).  Otherwise, the
 
3233
 *   given current score (cur) and turncount are displayed, and saved in
 
3234
 *   case cur==-1 on the next call.  
 
3235
 */
 
3236
void os_score(int cur, int turncount)
 
3237
{
 
3238
    char buf[20];
 
3239
 
 
3240
    /* check for the special -1 turn count */
 
3241
    if (turncount == -1)
 
3242
    {
 
3243
        /* it's turn "-1" - we're simply redrawing the score */
 
3244
        os_strsc((char *)0);
 
3245
    }
 
3246
    else
 
3247
    {
 
3248
        /* format the score */
 
3249
        sprintf(buf, "%d/%d", cur, turncount);
 
3250
 
 
3251
        /* display the score string */
 
3252
        os_strsc(buf);
 
3253
    }
 
3254
}
 
3255
 
 
3256
 
 
3257
/* ------------------------------------------------------------------------ */
 
3258
/*
 
3259
 *   Set the terminal into 'plain' mode: disables status line,
 
3260
 *   scrollback, command editing.
 
3261
 */
 
3262
void os_plain(void)
 
3263
{
 
3264
    /* set the 'plain' mode flag */
 
3265
    os_f_plain = 1;
 
3266
 
 
3267
    /* 
 
3268
     *   if we're running without a stdin, turn off pagination - since the
 
3269
     *   user won't be able to respond to [more] prompts, there's no reason
 
3270
     *   to show them 
 
3271
     */
 
3272
    if (oss_eof_on_stdin())
 
3273
        G_os_moremode = FALSE;
 
3274
}
 
3275
 
 
3276
/*
 
3277
 *   display text to the default window 
 
3278
 */
 
3279
void os_printz(const char *str)
 
3280
{
 
3281
    /* write using the base counted-length routine */
 
3282
    os_print(str, strlen(str));
 
3283
}
 
3284
 
 
3285
/*
 
3286
 *   display text to the default window
 
3287
 */
 
3288
void os_print(const char *str, size_t len)
 
3289
{
 
3290
    osgen_txtwin_t *win;
 
3291
    const char *p;
 
3292
    const char *startp;
 
3293
    size_t rem;
 
3294
 
 
3295
    /* determine what to do based on the status mode */
 
3296
    switch(status_mode)
 
3297
    {
 
3298
    case 2:
 
3299
        /* we're in the post-status-line mode - suppress all output */
 
3300
        break; 
 
3301
        
 
3302
    case 0:
 
3303
        /* we're showing the text in the default window */
 
3304
        if (os_f_plain)
 
3305
        {
 
3306
            /* plain mode - simply write it to stdout */
 
3307
            printf("%.*s", (int)len, str);
 
3308
        }
 
3309
        else
 
3310
        {
 
3311
            /* normal mode - write to the default window, if there is one */
 
3312
            win = S_default_win;
 
3313
            if (win != 0)
 
3314
            {
 
3315
                /* write the text to the window buffer */
 
3316
                ossaddsb(win, str, len, TRUE);
 
3317
 
 
3318
                /* 
 
3319
                 *   move the cursor to the new location, if we're not hiding
 
3320
                 *   updates for the moment 
 
3321
                 */
 
3322
                if (!S_deferred_redraw
 
3323
                    && !(win->base.flags & OSGEN_DEFER_REDRAW))
 
3324
                    ossloc(win->base.winy + win->base.y - win->base.scrolly,
 
3325
                           win->base.winx + win->base.x - win->base.scrollx);
 
3326
            }
 
3327
        }
 
3328
 
 
3329
        /* done */
 
3330
        break;
 
3331
        
 
3332
    case 1:
 
3333
        /* 
 
3334
         *   Status line contents.  Ignore the status line in 'plain' mode
 
3335
         *   or if there's no statusline window.  
 
3336
         */
 
3337
        if (os_f_plain || (win = S_status_win) == 0)
 
3338
            break;
 
3339
 
 
3340
        /* 
 
3341
         *   Skip leading newlines at the start of the statusline output.
 
3342
         *   Only do this if we don't already have anything buffered, since
 
3343
         *   a newline after some other text indicates the end of the status
 
3344
         *   line and thus can't be ignored.  
 
3345
         */
 
3346
        p = str;
 
3347
        rem = len;
 
3348
        if (win->base.winy == 0 || win->base.winx == 0)
 
3349
        {
 
3350
            /* the buffer is empty, so skip leading newlines */
 
3351
            for ( ; rem != 0 && *p == '\n' ; ++p, --rem) ;
 
3352
 
 
3353
            /* if that leaves nothing, we're done */
 
3354
            if (rem == 0)
 
3355
                break;
 
3356
 
 
3357
            /* 
 
3358
             *   add a space before the first character, so that we always
 
3359
             *   have a space at the left edge of the status line 
 
3360
             */
 
3361
            ossaddsb(win, " ", 1, TRUE);
 
3362
        }
 
3363
 
 
3364
        /* scan for a newline; if we find one, it's the end of the status */
 
3365
        for (startp = p ; rem != 0 && *p != '\n' ; ++p, --rem)
 
3366
        {
 
3367
            /* skip escapes */
 
3368
            switch (*p)
 
3369
            {
 
3370
            case OSGEN_ATTR:
 
3371
                /* skip the extra byte */
 
3372
                p += 1;
 
3373
                rem -= 1;
 
3374
                break;
 
3375
                
 
3376
            case OSGEN_COLOR:
 
3377
                /* skip the extra two bytes */
 
3378
                p += 2;
 
3379
                rem -= 2;
 
3380
                break;
 
3381
 
 
3382
            default:
 
3383
                /* everything else is one byte long */
 
3384
                break;
 
3385
            }
 
3386
        }
 
3387
 
 
3388
        /* add this text to the statusline window */
 
3389
        ossaddsb(win, startp, p - startp, TRUE);
 
3390
 
 
3391
        /* finish up if we found a newline */
 
3392
        if (*p == '\n')
 
3393
        {
 
3394
            /* switch to status mode 2 */
 
3395
            os_status(2);
 
3396
        }
 
3397
        
 
3398
        /* done */
 
3399
        break;
 
3400
    }
 
3401
}
 
3402
 
 
3403
void os_flush(void)
 
3404
{
 
3405
    /* 
 
3406
     *   we don't buffer output ourselves, so there's normally nothing to do
 
3407
     *   here; but if we're in 'plain' mode, let stdio know about the flush,
 
3408
     *   since it might be buffering output on our behalf 
 
3409
     */
 
3410
    if (os_f_plain)
 
3411
        fflush(stdout);
 
3412
}
 
3413
 
 
3414
void os_update_display(void)
 
3415
{
 
3416
    /* there's nothing we need to do */
 
3417
}
 
3418
 
 
3419
# ifdef USE_HISTORY
 
3420
/*
 
3421
 *   For command line history, we must have some buffer space to store
 
3422
 *   past command lines.  We will use a circular buffer:  when we move
 
3423
 *   the pointer past the end of the buffer, it wraps back to the start
 
3424
 *   of the buffer.  A "tail" indicates the oldest line in the buffer;
 
3425
 *   when we need more room for new text, we advance the tail and thereby
 
3426
 *   lose the oldest text in the buffer.
 
3427
 */
 
3428
static osfar_t char *histbuf = 0;
 
3429
static osfar_t char *histhead = 0;
 
3430
static osfar_t char *histtail = 0;
 
3431
 
 
3432
/*
 
3433
 *   ossadvhp advances a history pointer, and returns the new pointer.
 
3434
 *   This function takes the circular nature of the buffer into account
 
3435
 *   by wrapping back to the start of the buffer when it hits the end.
 
3436
 */
 
3437
char *ossadvhp(char *p)
 
3438
{
 
3439
    if (++p >= histbuf + HISTBUFSIZE)
 
3440
        p = histbuf;
 
3441
    return p;
 
3442
}
 
3443
 
 
3444
/*
 
3445
 *   ossdechp decrements a history pointer, wrapping the pointer back
 
3446
 *   to the top of the buffer when it reaches the bottom.
 
3447
 */
 
3448
char *ossdechp(char *p)
 
3449
{
 
3450
    if (p == histbuf)
 
3451
        p = histbuf + HISTBUFSIZE;
 
3452
    return p - 1;
 
3453
}
 
3454
 
 
3455
/*
 
3456
 *  osshstcpy copies from a history buffer into a contiguous destination
 
3457
 *  buffer, wrapping the history pointer if need be.  One null-terminated
 
3458
 *  string is copied.
 
3459
 */
 
3460
void osshstcpy(char *dst, char *hst)
 
3461
{
 
3462
    while (*hst != '\0')
 
3463
    {
 
3464
        *dst++ = *hst;
 
3465
        hst = ossadvhp(hst);
 
3466
    }
 
3467
    *dst = '\0';
 
3468
}
 
3469
 
 
3470
/*
 
3471
 *   ossprvcmd returns a pointer to the previous history command, given
 
3472
 *   a pointer to a history command.  It returns a null pointer if the
 
3473
 *   given history command is the first in the buffer.
 
3474
 */
 
3475
char *ossprvcmd(char *hst)
 
3476
{
 
3477
    /* check to see if we're already at the fist command */
 
3478
    if (hst == histtail)
 
3479
        return 0;
 
3480
 
 
3481
    /* back up to the previous null byte */
 
3482
    hst = ossdechp(hst);
 
3483
 
 
3484
    /* scan back to the previous line */
 
3485
    do
 
3486
    {
 
3487
        hst = ossdechp(hst);
 
3488
    } while (*hst && hst != histtail);
 
3489
 
 
3490
    /* step over the null byte to the start of the following line */
 
3491
    if (*hst == 0)
 
3492
        hst = ossadvhp(hst);
 
3493
 
 
3494
    /* return the result */
 
3495
    return hst;
 
3496
}
 
3497
 
 
3498
/*
 
3499
 *   ossnxtcmd returns a pointer to the next history command, given
 
3500
 *   a pointer to a history command.  It returns a null pointer if the
 
3501
 *   given command is already past the last command.
 
3502
 */
 
3503
char *ossnxtcmd(char *hst)
 
3504
{
 
3505
    /* check to see if we're already past the last line */
 
3506
    if (hst == histhead)
 
3507
        return 0;
 
3508
 
 
3509
    /* scan forward to the next null byte */
 
3510
    while(*hst != '\0')
 
3511
        hst = ossadvhp(hst);
 
3512
 
 
3513
    /* scan past the null onto the new command */
 
3514
    hst = ossadvhp(hst);
 
3515
 
 
3516
    /* return the pointer */
 
3517
    return hst;
 
3518
}
 
3519
# endif /* USE_HISTORY */
 
3520
 
 
3521
/* ------------------------------------------------------------------------ */
 
3522
/*
 
3523
 *   Display an input line under construction from the given character
 
3524
 *   position.  If 'delta_yscroll' is non-null, then we'll scroll the window
 
3525
 *   vertically if necessary and fill in '*delta_yscroll' with the number of
 
3526
 *   lines we scrolled by.  
 
3527
 */
 
3528
static void ossdsp_str(osgen_win_t *win, int y, int x, int color,
 
3529
                       char *str, size_t len, int *delta_yscroll)
 
3530
{
 
3531
    /* presume we won't scroll */
 
3532
    if (delta_yscroll != 0)
 
3533
        *delta_yscroll = 0;
 
3534
 
 
3535
    /* keep going until we exhaust the string */
 
3536
    while (len != 0)
 
3537
    {
 
3538
        size_t cur;
 
3539
        unsigned char oldc;
 
3540
 
 
3541
        /* display as much as will fit on the current line before wrapping */
 
3542
        cur = win->winx + win->wid - x;
 
3543
        if (cur > len)
 
3544
            cur = len;
 
3545
 
 
3546
        /* null-terminate the chunk, but save the original character */
 
3547
        oldc = str[cur];
 
3548
        str[cur] = '\0';
 
3549
 
 
3550
        /* display this chunk */
 
3551
        ossdsp(y, x, color, (char *)str);
 
3552
 
 
3553
        /* restore the character where we put our null */
 
3554
        str[cur] = oldc;
 
3555
 
 
3556
        /* move our string counters past this chunk */
 
3557
        str += cur;
 
3558
        len -= cur;
 
3559
 
 
3560
        /* advance the x position */
 
3561
        x += cur;
 
3562
 
 
3563
        /* if we've reached the right edge of the window, wrap the line */
 
3564
        if (x >= win->winx + (int)win->wid)
 
3565
        {
 
3566
            /* wrap to the left edge of the window */
 
3567
            x = win->winx;
 
3568
 
 
3569
            /* advance to the next line */
 
3570
            ++y;
 
3571
 
 
3572
            /* 
 
3573
             *   if this puts us past the bottom of the window, and we're
 
3574
             *   allowed to scroll the window, do so
 
3575
             */
 
3576
            if (y >= win->winy + (int)win->ht && delta_yscroll != 0)
 
3577
            {
 
3578
                /* scroll by one line */
 
3579
                ossscr(win->winy, win->winx,
 
3580
                       win->winy + win->ht - 1, win->winx + win->wid - 1,
 
3581
                       win->oss_fillcolor);
 
3582
 
 
3583
                /* adjust the scroll position of the window */
 
3584
                win->scrolly++;
 
3585
 
 
3586
                /* count the scrolling */
 
3587
                ++(*delta_yscroll);
 
3588
 
 
3589
                /* move back a line */
 
3590
                --y;
 
3591
            }
 
3592
        }
 
3593
    }
 
3594
}
 
3595
 
 
3596
/*
 
3597
 *   Move the cursor left by the number of characters.  
 
3598
 */
 
3599
static void oss_gets_csrleft(osgen_txtwin_t *win, int *y, int *x, size_t len)
 
3600
{
 
3601
    for ( ; len != 0 ; --len)
 
3602
    {
 
3603
        /* move left one character, wrapping at the end of the line */
 
3604
        if (--*x < win->base.winx)
 
3605
        {
 
3606
            /* move up a line */
 
3607
            --*y;
 
3608
 
 
3609
            /* move to the end of the line */
 
3610
            *x = win->base.winx + win->base.wid - 1;
 
3611
        }
 
3612
    }
 
3613
}
 
3614
 
 
3615
/*
 
3616
 *   Move the cursor right by the number of characters.  
 
3617
 */
 
3618
static void oss_gets_csrright(osgen_txtwin_t *win, int *y, int *x, size_t len)
 
3619
{
 
3620
    for ( ; len != 0 ; --len)
 
3621
    {
 
3622
        /* move right one character, wrapping at the end of the line */
 
3623
        if (++*x >= win->base.winx + (int)win->base.wid)
 
3624
        {
 
3625
            /* move down a line */
 
3626
            ++*y;
 
3627
 
 
3628
            /* move to the left column */
 
3629
            *x = win->base.winx;
 
3630
        }
 
3631
    }
 
3632
}
 
3633
 
 
3634
/*
 
3635
 *   clear an area of the display formerly occupied by some input text 
 
3636
 */
 
3637
static void oss_gets_clear(osgen_txtwin_t *win, int y, int x, size_t len)
 
3638
{
 
3639
    /* if we don't even reach the left edge of the window, ignore it */
 
3640
    if (x + (int)len <= win->base.winx)
 
3641
        return;
 
3642
 
 
3643
    /* skip anything to the left of the window */
 
3644
    if (x < win->base.winx)
 
3645
    {
 
3646
        /* deduct the unseen part from the length */
 
3647
        len -= (win->base.winx - x);
 
3648
 
 
3649
        /* start at the left edge */
 
3650
        x = win->base.winx;
 
3651
    }
 
3652
 
 
3653
    /* clear one line at a time */
 
3654
    while (len != 0)
 
3655
    {
 
3656
        size_t cur;
 
3657
 
 
3658
        /* calculate how much we have left to the right edge of the window */
 
3659
        cur = win->base.winx + win->base.wid - x;
 
3660
 
 
3661
        /* limit the clearing to the requested length */
 
3662
        if (cur > len)
 
3663
            cur = len;
 
3664
 
 
3665
        /* clear this chunk */
 
3666
        ossclr(y, x, y, x + cur - 1, win->base.oss_fillcolor);
 
3667
 
 
3668
        /* deduct this chunk from the remaining length */
 
3669
        len -= cur;
 
3670
 
 
3671
        /* move to the start of the next line */
 
3672
        x = win->base.winx;
 
3673
        ++y;
 
3674
 
 
3675
        /* if we're past the bottom of the window, stop */
 
3676
        if (y >= win->base.winy + (int)win->base.ht)
 
3677
            break;
 
3678
    }
 
3679
}
 
3680
 
 
3681
/*
 
3682
 *   Delete a character in the buffer, updating the display. 
 
3683
 */
 
3684
static void oss_gets_delchar(osgen_txtwin_t *win,
 
3685
                             char *buf, char *p, char **eol, int x, int y)
 
3686
{
 
3687
    int color;
 
3688
    
 
3689
    /* get the oss color for the current text in the window */
 
3690
    color = ossgetcolor(win->base.txtfg, win->base.txtbg,
 
3691
                        win->base.txtattr, win->base.fillcolor);
 
3692
 
 
3693
    /* if the character is within the buffer, delete it */
 
3694
    if (p < *eol)
 
3695
    {
 
3696
        /* delete the character and close the gap */
 
3697
        --*eol;
 
3698
        if (p != *eol)
 
3699
            memmove(p, p + 1, *eol - p);
 
3700
 
 
3701
        /* null-terminate the shortened buffer */
 
3702
        **eol = '\0';
 
3703
        
 
3704
        /* re-display the changed part of the string */
 
3705
        ossdsp_str(&win->base, y, x, color, p, *eol - p, 0);
 
3706
 
 
3707
        /* move to the position of the former last character */
 
3708
        oss_gets_csrright(win, &y, &x, *eol - p);
 
3709
 
 
3710
        /* clear the screen area where the old last character was displayed */
 
3711
        ossclr(y, x, y, x, win->base.oss_fillcolor);
 
3712
    }
 
3713
}
 
3714
 
 
3715
/*
 
3716
 *   Backspace in the buffer, updating the display and adjusting the cursor
 
3717
 *   position. 
 
3718
 */
 
3719
static void oss_gets_backsp(osgen_txtwin_t *win,
 
3720
                            char *buf, char **p,
 
3721
                            char **eol, int *x, int *y)
 
3722
{
 
3723
    int color;
 
3724
 
 
3725
    /* get the oss color for the current text in the window */
 
3726
    color = ossgetcolor(win->base.txtfg, win->base.txtbg,
 
3727
                        win->base.txtattr, win->base.fillcolor);
 
3728
 
 
3729
    /* if we can back up, do so */
 
3730
    if (*p > buf)
 
3731
    {
 
3732
        int tmpy;
 
3733
        int tmpx;
 
3734
 
 
3735
        /* move our insertion point back one position */
 
3736
        --*p;
 
3737
        
 
3738
        /* the line is now one character shorter */
 
3739
        --*eol;
 
3740
 
 
3741
        /* shift all of the characters down one position */
 
3742
        if (*p != *eol)
 
3743
            memmove(*p, *p + 1, *eol - *p);
 
3744
 
 
3745
        /* move the cursor back, wrapping if at the first column */
 
3746
        if (--*x < win->base.winx)
 
3747
        {
 
3748
            *x = win->base.winx + win->base.wid - 1;
 
3749
            --*y;
 
3750
        }
 
3751
 
 
3752
        /* null-terminate the shortened buffer */
 
3753
        **eol = '\0';
 
3754
 
 
3755
        /* 
 
3756
         *   display the string from the current position, so that we update
 
3757
         *   the display for the moved characters 
 
3758
         */
 
3759
        tmpy = *y;
 
3760
        tmpx = *x;
 
3761
        ossdsp_str(&win->base, tmpy, tmpx, color, *p, *eol - *p, 0);
 
3762
 
 
3763
        /* clear the screen area where the old last character was shown */
 
3764
        oss_gets_csrright(win, &tmpy, &tmpx, *eol - *p);
 
3765
        ossclr(tmpy, tmpx, tmpy, tmpx, win->base.oss_fillcolor);
 
3766
    }
 
3767
}
 
3768
 
 
3769
/*
 
3770
 *   Redraw any command line under construction 
 
3771
 */
 
3772
static void osgen_gets_redraw_cmdline(void)
 
3773
{
 
3774
    osgen_txtwin_t *win;
 
3775
    int x, y;
 
3776
    int color;
 
3777
 
 
3778
    /* 
 
3779
     *   get the default window; if we don't have one, or there's no command
 
3780
     *   line editing in progress, there's nothing to do 
 
3781
     */
 
3782
    if ((win = S_default_win) == 0 || !S_gets_in_progress)
 
3783
        return;
 
3784
 
 
3785
    /* set up at the current cursor position */
 
3786
    x = S_gets_x;
 
3787
    y = S_gets_y;
 
3788
 
 
3789
    /* move to the start of the command */
 
3790
    oss_gets_csrleft(win, &y, &x, S_gets_ofs);
 
3791
 
 
3792
    /* get the color of the command text */
 
3793
    color = ossgetcolor(win->base.txtfg, win->base.txtbg,
 
3794
                        win->base.txtattr, win->base.fillcolor);
 
3795
 
 
3796
    /* draw the command line */
 
3797
    ossdsp_str(&win->base, y, x, color, S_gets_buf, strlen(S_gets_buf), 0);
 
3798
}
 
3799
 
 
3800
/* ------------------------------------------------------------------------ */
 
3801
/*
 
3802
 *   cancel interrupted input 
 
3803
 */
 
3804
void os_gets_cancel(int reset)
 
3805
{
 
3806
    int x, y;
 
3807
 
 
3808
    /* if we're doing scrollback, cancel it */
 
3809
    if (S_sbmode_win != 0)
 
3810
        osgen_sb_mode_end();
 
3811
 
 
3812
    /* do any deferred redrawing */
 
3813
    osssb_redraw_if_needed();
 
3814
 
 
3815
    /* 
 
3816
     *   if we interrupted a previous line, apply display effects as though
 
3817
     *   the user had pressed return 
 
3818
     */
 
3819
    if (S_gets_in_progress)
 
3820
    {
 
3821
        osgen_txtwin_t *win;
 
3822
 
 
3823
        /* use the main window */
 
3824
        win = S_main_win;
 
3825
 
 
3826
        /* move to the end of the input line */
 
3827
        x = S_gets_x;
 
3828
        y = S_gets_y;
 
3829
        oss_gets_csrright(win, &y, &x, strlen(S_gets_buf + S_gets_ofs));
 
3830
        
 
3831
        /* set the cursor to the new position */
 
3832
        ossloc(y, x);
 
3833
        
 
3834
        /* copy the buffer to the screen save buffer, adding a newline */
 
3835
        ossaddsb_input(win, (char *)S_gets_buf, TRUE);
 
3836
 
 
3837
        /* we no longer have an input in progress */
 
3838
        S_gets_in_progress = FALSE;
 
3839
    }
 
3840
    
 
3841
    /* if we're resetting, clear our saved buffer */
 
3842
    if (reset)
 
3843
        S_gets_buf[0] = '\0';
 
3844
}
 
3845
 
 
3846
/* ------------------------------------------------------------------------ */
 
3847
/*
 
3848
 *   Initialize input line editing mode.  Returns true if we can
 
3849
 *   successfully set up input line editing mode, false if not.  
 
3850
 */
 
3851
int os_gets_begin(size_t max_line_len)
 
3852
{
 
3853
    osgen_txtwin_t *win;
 
3854
 
 
3855
    /* if there's no default window, there's nothing we can do */
 
3856
    if ((win = S_default_win) == 0)
 
3857
        return FALSE;
 
3858
 
 
3859
    /* do any deferred redrawing */
 
3860
    osssb_redraw_if_needed();
 
3861
 
 
3862
    /* if the cursor if off the screen vertically, scroll to show it */
 
3863
    if (win->base.y >= win->base.scrolly + (int)win->base.ht)
 
3864
        osgen_scroll_win_fwd(win, win->base.y
 
3865
                             - (win->base.scrolly + win->base.ht) + 1);
 
3866
 
 
3867
    /* if we're horizontally scrolled, scroll to the left edge */
 
3868
    if (win->base.scrollx != 0)
 
3869
    {
 
3870
        /* scroll to the left edge */
 
3871
        win->base.scrollx = 0;
 
3872
 
 
3873
        /* redraw the window */
 
3874
        osgen_redraw_win(&win->base);
 
3875
    }
 
3876
 
 
3877
# ifdef USE_HISTORY
 
3878
    /* allocate the history buffer if it's not already allocated */
 
3879
    if (histbuf == 0)
 
3880
    {
 
3881
        histbuf = (char *)osmalloc(HISTBUFSIZE);
 
3882
        histhead = histtail = histbuf;
 
3883
        S_gets_curhist = histhead;
 
3884
    }
 
3885
# endif /* USE_HISTORY */
 
3886
 
 
3887
    /*
 
3888
     *   If we have saved input state from a previous interrupted call,
 
3889
     *   restore it now.  Otherwise, initialize everything.  
 
3890
     */
 
3891
    if (S_gets_buf[0] != '\0' || S_gets_in_progress)
 
3892
    {
 
3893
        /* 
 
3894
         *   if we cancelled the previous input, we must re-display the
 
3895
         *   buffer under construction, since we have displayed something
 
3896
         *   else in between and have re-displayed the prompt 
 
3897
         */
 
3898
        if (!S_gets_in_progress)
 
3899
        {
 
3900
            int x;
 
3901
            int y;
 
3902
            int deltay;
 
3903
            int color;
 
3904
 
 
3905
            /* set up at the window's output position, in screen coords */
 
3906
            x = win->base.winx + win->base.x - win->base.scrollx;
 
3907
            y = win->base.winy + win->base.y - win->base.scrolly;
 
3908
 
 
3909
            /* get the current color in the window */
 
3910
            color = ossgetcolor(win->base.txtfg, win->base.txtbg,
 
3911
                                win->base.txtattr, win->base.fillcolor);
 
3912
 
 
3913
            /* re-display the buffer */
 
3914
            ossdsp_str(&win->base, y, x, color,
 
3915
                       S_gets_buf, strlen(S_gets_buf), &deltay);
 
3916
 
 
3917
            /* adjust our y position for any scrolling we just did */
 
3918
            y -= deltay;
 
3919
 
 
3920
            /* limit the initial offset to the available buffer length */
 
3921
            if (S_gets_ofs > (int)max_line_len)
 
3922
                S_gets_ofs = max_line_len;
 
3923
 
 
3924
            /* move back to the original insertion point */
 
3925
            oss_gets_csrright(win, &y, &x, S_gets_ofs);
 
3926
 
 
3927
            /* note the current position as the new editing position */
 
3928
            S_gets_x = x;
 
3929
            S_gets_y = y;
 
3930
        }
 
3931
    }
 
3932
    else
 
3933
    {
 
3934
        /* initialize our history recall pointer */
 
3935
        S_gets_curhist = histhead;
 
3936
 
 
3937
        /* set up at the window's output position, in screen coords */
 
3938
        S_gets_x = win->base.winx + win->base.x - win->base.scrollx;
 
3939
        S_gets_y = win->base.winy + win->base.y - win->base.scrolly;
 
3940
 
 
3941
        /* we're at offset zero in the input line */
 
3942
        S_gets_ofs = 0;
 
3943
    }
 
3944
 
 
3945
    /* 
 
3946
     *   set the buffer end pointer to limit input to the maximum size the
 
3947
     *   caller has requested, or the maximum size of our internal buffer,
 
3948
     *   whichever is smaller 
 
3949
     */
 
3950
    if (max_line_len > S_gets_buf_siz)
 
3951
        S_gets_buf_end = S_gets_buf + S_gets_buf_siz - 1;
 
3952
    else
 
3953
        S_gets_buf_end = S_gets_buf + max_line_len - 1;
 
3954
 
 
3955
    /* override the default cursor position logic while reading */
 
3956
    S_special_cursor_pos = TRUE;
 
3957
    S_special_cursor_x = S_gets_x;
 
3958
    S_special_cursor_y = S_gets_y;
 
3959
 
 
3960
    /* note that input is in progress */
 
3961
    S_gets_in_progress = TRUE;
 
3962
 
 
3963
    /* successfully set up */
 
3964
    return TRUE;
 
3965
}
 
3966
 
 
3967
 
 
3968
/* ------------------------------------------------------------------------ */
 
3969
/*
 
3970
 *   Process an event in input line editing mode.  Returns true if the user
 
3971
 *   explicitly ended input line editing by pressing Return or something
 
3972
 *   similar, false if input line editing mode remains active.  
 
3973
 */
 
3974
int os_gets_process(int event_type, os_event_info_t *event_info)
 
3975
{
 
3976
    unsigned char c;
 
3977
    char *p;
 
3978
    char *eol;
 
3979
    char *buf;
 
3980
    int x;
 
3981
    int y;
 
3982
    osgen_txtwin_t *win;
 
3983
    int color;
 
3984
 
 
3985
    /* use the default window */
 
3986
    if ((win = S_default_win) == 0)
 
3987
        return TRUE;
 
3988
 
 
3989
    /* if it's a keystroke event, convert from "raw" to a CMD_xxx code */
 
3990
    if (event_type == OS_EVT_KEY)
 
3991
        oss_raw_key_to_cmd(event_info);
 
3992
 
 
3993
    /* 
 
3994
     *   if we're in scrollback mode, run the event through the scrollback
 
3995
     *   handler rather than handling it directly 
 
3996
     */
 
3997
    if (S_sbmode_win != 0)
 
3998
    {
 
3999
        /* run the event through scrollback mode */
 
4000
        if (osgen_sb_mode(win, event_type, event_info))
 
4001
        {
 
4002
            /* 
 
4003
             *   scrollback mode fully handled the event, so we're done;
 
4004
             *   tell the caller we didn't finish command input editing 
 
4005
             */
 
4006
            return FALSE;
 
4007
        }
 
4008
    }
 
4009
 
 
4010
    /* ignore everything except keystroke events */
 
4011
    if (event_type != OS_EVT_KEY)
 
4012
        return FALSE;
 
4013
 
 
4014
    /* get the key from the event */
 
4015
    c = (unsigned char)event_info->key[0];
 
4016
 
 
4017
    /* set up at the current position */
 
4018
    x = S_gets_x;
 
4019
    y = S_gets_y;
 
4020
 
 
4021
    /* set up our buffer pointers */
 
4022
    p = S_gets_buf + S_gets_ofs;
 
4023
    buf = S_gets_buf;
 
4024
    eol = p + strlen(p);
 
4025
 
 
4026
    /* get the current color in the window */
 
4027
    color = ossgetcolor(win->base.txtfg, win->base.txtbg,
 
4028
                        win->base.txtattr, win->base.fillcolor);
 
4029
 
 
4030
    /* 
 
4031
     *   Check the character we got.  Note that we must interpret certain
 
4032
     *   control characters explicitly, because os_get_event() returns raw
 
4033
     *   keycodes (untranslated into CMD_xxx codes) for control characters.  
 
4034
     */
 
4035
    switch(c)
 
4036
    {
 
4037
    case 8:
 
4038
        /* backspace one character */
 
4039
        oss_gets_backsp(win, buf, &p, &eol, &x, &y);
 
4040
        break;
 
4041
        
 
4042
    case 13:
 
4043
        /* Return/Enter key - we're done.  Null-terminate the input. */
 
4044
        *eol = '\0';
 
4045
        
 
4046
        /* move to the end of the line */
 
4047
        oss_gets_csrright(win, &y, &x, eol - p);
 
4048
        p = eol;
 
4049
 
 
4050
# ifdef USE_HISTORY
 
4051
        /*
 
4052
         *   Save the line in our history buffer.  If we don't have enough
 
4053
         *   room, lose some old text by advancing the tail pointer far
 
4054
         *   enough.  Don't save it if it's a blank line, though, or if it
 
4055
         *   duplicates the most recent previous command.  
 
4056
         */
 
4057
        if (strlen(buf) != 0)
 
4058
        {
 
4059
            char *q;
 
4060
            int advtail;
 
4061
            int saveflag = 1;                /* assume we will be saving it */
 
4062
            
 
4063
            if (q = ossprvcmd(histhead))
 
4064
            {
 
4065
                char *p = buf;
 
4066
                
 
4067
                while (*p == *q && *p != '\0' && *q != '\0')
 
4068
                {
 
4069
                    ++p;
 
4070
                    q = ossadvhp(q);
 
4071
                }
 
4072
                if (*p == *q)               /* is this a duplicate command? */
 
4073
                    saveflag = 0;                   /* if so, don't save it */
 
4074
            }
 
4075
 
 
4076
            if (saveflag)
 
4077
            {
 
4078
                for (q = buf, advtail = 0 ; q <= eol ; ++q)
 
4079
                {
 
4080
                    *histhead = *q;
 
4081
                    histhead = ossadvhp(histhead);
 
4082
                    if (histhead == histtail)
 
4083
                    {
 
4084
                        histtail = ossadvhp(histtail);
 
4085
                        advtail = 1;
 
4086
                    }
 
4087
                }
 
4088
 
 
4089
                /*
 
4090
                 *   If we have encroached on space that was already
 
4091
                 *   occupied, throw away the entire command we have
 
4092
                 *   partially trashed; to do so, advance the tail pointer
 
4093
                 *   to the next null byte.  
 
4094
                 */
 
4095
                if (advtail)
 
4096
                {
 
4097
                    while(*histtail)
 
4098
                        histtail = ossadvhp(histtail);
 
4099
                    histtail = ossadvhp(histtail);
 
4100
                }
 
4101
            }
 
4102
        }
 
4103
# endif /* USE_HISTORY */
 
4104
 
 
4105
        /* add the text to the scrollback buffer */
 
4106
        ossaddsb_input(win, buf, TRUE);
 
4107
 
 
4108
        /* done with the special cursor position */
 
4109
        S_special_cursor_pos = FALSE;
 
4110
 
 
4111
        /* 
 
4112
         *   immediately scroll the window if necessary, and make sure the
 
4113
         *   cursor is showing at the right location 
 
4114
         */
 
4115
        osgen_auto_vscroll(win);
 
4116
        osssb_cursor_to_default_pos();
 
4117
 
 
4118
        /* input is no longer in progress */
 
4119
        S_gets_in_progress = FALSE;
 
4120
 
 
4121
        /* tell the caller we're done */
 
4122
        return TRUE;
 
4123
 
 
4124
    case 0:
 
4125
        /* extended key code - get the second half of the code */
 
4126
        c = (unsigned char)event_info->key[1];
 
4127
 
 
4128
        /* handle the command key code */
 
4129
        switch(c)
 
4130
        {
 
4131
# ifdef USE_SCROLLBACK
 
4132
        case CMD_SCR:
 
4133
        case CMD_PGUP:
 
4134
        case CMD_PGDN:
 
4135
            /* run the event through scrollback mode */
 
4136
            osgen_sb_mode(win, event_type, event_info);
 
4137
 
 
4138
            /* done */
 
4139
            break;
 
4140
# endif /* USE_SCROLLBACK */
 
4141
 
 
4142
        case CMD_LEFT:
 
4143
            /* move the cursor left */
 
4144
            if (p > buf)
 
4145
            {
 
4146
                --p;
 
4147
                oss_gets_csrleft(win, &y, &x, 1);
 
4148
            }
 
4149
            break;
 
4150
 
 
4151
        case CMD_WORD_LEFT:
 
4152
            /*
 
4153
             *   Move back one word.  This moves the cursor back a
 
4154
             *   character, then seeks back until we're on a non-space
 
4155
             *   character, then seeks back until we're on a character
 
4156
             *   preceded by a space character.  
 
4157
             */
 
4158
            if (p > buf)
 
4159
            {
 
4160
                /* back up one character */
 
4161
                --p;
 
4162
                oss_gets_csrleft(win, &y, &x, 1);
 
4163
 
 
4164
                /* back up until we're on a non-space character */
 
4165
                while (p > buf && t_isspace(*p) && !t_isspace(*(p-1)))
 
4166
                {
 
4167
                    --p;
 
4168
                    oss_gets_csrleft(win, &y, &x, 1);
 
4169
                }
 
4170
 
 
4171
                /* 
 
4172
                 *   back up again until we're on a character preceded by a
 
4173
                 *   space character 
 
4174
                 */
 
4175
                while (p > buf && !t_isspace(*(p-1)))
 
4176
                {
 
4177
                    --p;
 
4178
                    oss_gets_csrleft(win, &y, &x, 1);
 
4179
                }
 
4180
            }
 
4181
            break;
 
4182
 
 
4183
        case CMD_RIGHT:
 
4184
            /* move the cursor right */
 
4185
            if (p < eol)
 
4186
            {
 
4187
                ++p;
 
4188
                oss_gets_csrright(win, &y, &x, 1);
 
4189
            }
 
4190
            break;
 
4191
 
 
4192
        case CMD_WORD_RIGHT:
 
4193
            /*
 
4194
             *   Move right one word.  This moves the cursor right until
 
4195
             *   we're on a space character, then moves the cursor right
 
4196
             *   again until we're on a non-space character.  First, move
 
4197
             *   right until we're between words (i.e., until we're on a
 
4198
             *   space character).  
 
4199
             */
 
4200
            while (p < eol && !t_isspace(*p))
 
4201
            {
 
4202
                ++p;
 
4203
                oss_gets_csrright(win, &y, &x, 1);
 
4204
            }
 
4205
 
 
4206
            /* now move right until we're on a non-space character */
 
4207
            while (p < eol && t_isspace(*p))
 
4208
            {
 
4209
                ++p;
 
4210
                oss_gets_csrright(win, &y, &x, 1);
 
4211
            }
 
4212
            break;
 
4213
 
 
4214
        case CMD_DEL:
 
4215
            /* delete a character */
 
4216
            oss_gets_delchar(win, buf, p, &eol, x, y);
 
4217
            break;
 
4218
 
 
4219
#ifdef UNIX               
 
4220
        case CMD_WORDKILL:
 
4221
            {
 
4222
                /* remove spaces preceding word */
 
4223
                while (p >= buf && *p <= ' ')
 
4224
                    oss_gets_backsp(win, buf, &p, &eol, &x, &y);
 
4225
 
 
4226
                /* remove previous word (i.e., until we get a space) */
 
4227
                while (p >= buf && *p > ' ')
 
4228
                    oss_gets_backsp(win, buf, &p, &eol, &x, &y);
 
4229
 
 
4230
                /* that's it */
 
4231
                break;
 
4232
            }
 
4233
#endif /* UNIX */
 
4234
 
 
4235
        case CMD_KILL:
 
4236
        case CMD_HOME:
 
4237
# ifdef USE_HISTORY
 
4238
        case CMD_UP:
 
4239
        case CMD_DOWN:
 
4240
            /*
 
4241
             *   Home, Kill (delete entire line), History Up, History Down -
 
4242
             *   what these all have in common is that we move to the start
 
4243
             *   of the line before doing anything else.  
 
4244
             */
 
4245
 
 
4246
            /* if 'up', make sure we have more history to traverse */
 
4247
            if (c == CMD_UP && !ossprvcmd(S_gets_curhist))
 
4248
                break;
 
4249
 
 
4250
            /* if 'down', make sure there's more history to traverse */
 
4251
            if (c == CMD_DOWN && !ossnxtcmd(S_gets_curhist))
 
4252
                break;
 
4253
 
 
4254
            /* 
 
4255
             *   if this is the first 'up', save the current buffer, so that
 
4256
             *   we can reinstate it if we traverse back 'down' until we're
 
4257
             *   back at the original buffer (the active buffer essentially
 
4258
             *   becomes a temporary history entry that we can recover by
 
4259
             *   history-scrolling back down to it) 
 
4260
             */
 
4261
                if (c == CMD_UP && !ossnxtcmd(S_gets_curhist))
 
4262
                    strcpy(S_hist_sav, buf);
 
4263
 
 
4264
# endif /* USE_HISTORY */
 
4265
 
 
4266
                /* move to the start of the line */
 
4267
                if (p != buf)
 
4268
                {
 
4269
                    /* move the cursor */
 
4270
                    oss_gets_csrleft(win, &y, &x, p - buf);
 
4271
 
 
4272
                    /* move the insertion pointer */
 
4273
                    p = buf;
 
4274
                }
 
4275
 
 
4276
                /* if it was just a 'home' command, we're done */
 
4277
                if (c == CMD_HOME)
 
4278
                    break;
 
4279
 
 
4280
                /*
 
4281
                 *   We're at the start of the line now; fall through for
 
4282
                 *   KILL, UP, and DOWN to the code which deletes to the end
 
4283
                 *   of the line.  
 
4284
                 */
 
4285
 
 
4286
        case CMD_DEOL:
 
4287
            /* clear the remainder of the line on the display */
 
4288
            oss_gets_clear(win, y, x, eol - p);
 
4289
 
 
4290
            /* truncate the buffer at the insertion point */
 
4291
            eol = p;
 
4292
            *p = '\0';
 
4293
 
 
4294
# ifdef USE_HISTORY
 
4295
            if (c == CMD_UP)
 
4296
            {
 
4297
                S_gets_curhist = ossprvcmd(S_gets_curhist);
 
4298
                osshstcpy(buf, S_gets_curhist);
 
4299
            }
 
4300
            else if (c == CMD_DOWN)
 
4301
            {
 
4302
                if (!ossnxtcmd(S_gets_curhist))
 
4303
                    break;                                       /* no more */
 
4304
                S_gets_curhist = ossnxtcmd(S_gets_curhist);
 
4305
                if (ossnxtcmd(S_gets_curhist))        /* on a valid command */
 
4306
                    osshstcpy(buf, S_gets_curhist);        /* ... so use it */
 
4307
                else
 
4308
                {
 
4309
                    /* no more history - restore original line */
 
4310
                    strcpy(buf, S_hist_sav);
 
4311
                }
 
4312
            }
 
4313
            if ((c == CMD_UP || c == CMD_DOWN)
 
4314
                && strlen(buf) != 0)
 
4315
            {
 
4316
                int deltay;
 
4317
 
 
4318
                /* get the end pointer based on null termination */
 
4319
                eol = buf + strlen(buf);
 
4320
 
 
4321
                /* display the string */
 
4322
                ossdsp_str(&win->base, y, x, color, p, eol - p, &deltay);
 
4323
                y -= deltay;
 
4324
 
 
4325
                /* move to the end of the line */
 
4326
                oss_gets_csrright(win, &y, &x, eol - p);
 
4327
                p = eol;
 
4328
            }
 
4329
# endif /* USE_HISTORY */
 
4330
            break;
 
4331
        case CMD_END:
 
4332
            while (p < eol)
 
4333
            {
 
4334
                ++p;
 
4335
                if (++x >= win->base.winx + (int)win->base.wid)
 
4336
                {
 
4337
                    x = win->base.winx;
 
4338
                    ++y;
 
4339
                }
 
4340
            }
 
4341
            break;
 
4342
        }
 
4343
        break;
 
4344
 
 
4345
    default:
 
4346
        if (c >= ' ' && eol < S_gets_buf_end)
 
4347
        {
 
4348
            int deltay;
 
4349
 
 
4350
            /* open up the line and insert the character */
 
4351
            if (p != eol)
 
4352
                memmove(p + 1, p, eol - p);
 
4353
            ++eol;
 
4354
            *p = (char)c;
 
4355
            *eol = '\0';
 
4356
 
 
4357
            /* write the updated part of the line */
 
4358
            ossdsp_str(&win->base, y, x, color, p, eol - p, &deltay);
 
4359
            y -= deltay;
 
4360
 
 
4361
            /* move the cursor right one character */
 
4362
            oss_gets_csrright(win, &y, &x, 1);
 
4363
 
 
4364
            /* advance the buffer pointer one character */
 
4365
            ++p;
 
4366
        }
 
4367
        break;
 
4368
    }
 
4369
 
 
4370
    /* remember the current editing position */
 
4371
    S_special_cursor_x = S_gets_x = x;
 
4372
    S_special_cursor_y = S_gets_y = y;
 
4373
    S_gets_ofs = p - S_gets_buf;
 
4374
 
 
4375
    /* we didn't finish editing */
 
4376
    return FALSE;
 
4377
}
 
4378
 
 
4379
/* ------------------------------------------------------------------------ */
 
4380
/*
 
4381
 *   Common routine to read a command from the keyboard.  This
 
4382
 *   implementation provides command editing and history, as well as timeout
 
4383
 *   capabilities.
 
4384
 */
 
4385
int os_gets_timeout(unsigned char *buf, size_t bufl,
 
4386
                    unsigned long timeout, int use_timeout)
 
4387
{
 
4388
    long end_time;
 
4389
    osgen_txtwin_t *win;
 
4390
    
 
4391
    /* if we're in 'plain' mode, simply use stdio input */
 
4392
    if (os_f_plain)
 
4393
    {
 
4394
        size_t len;
 
4395
 
 
4396
        /* make sure the standard output is flushed */
 
4397
        fflush(stdout);
 
4398
 
 
4399
        /* we don't support the timeout feature in plain mode */
 
4400
        if (use_timeout)
 
4401
            return OS_EVT_NOTIMEOUT;
 
4402
 
 
4403
        /* 
 
4404
         *   get input from stdio, and translate the result code - if gets()
 
4405
         *   returns null, it indicates an error of some kind, so return an
 
4406
         *   end-of-file indication 
 
4407
         */
 
4408
        if (fgets((char *)buf, bufl, stdin) == 0)
 
4409
            return OS_EVT_EOF;
 
4410
 
 
4411
        /* remove the trailing newline from the buffer, if present */
 
4412
        if ((len = strlen((char *)buf)) != 0 && buf[len-1] == '\n')
 
4413
            buf[len-1] = '\0';
 
4414
 
 
4415
        /* indicate that we read a line */
 
4416
        return OS_EVT_LINE;
 
4417
    }
 
4418
 
 
4419
    /* begin input editing mode */
 
4420
    os_gets_begin(bufl);
 
4421
 
 
4422
    /*
 
4423
     *   If we have a timeout, calculate the system clock time at which the
 
4424
     *   timeout expires.  This is simply the current system clock time plus
 
4425
     *   the timeout interval.  Since we might need to process a series of
 
4426
     *   events, we'll need to know how much time remains at each point we
 
4427
     *   get a new event.  
 
4428
     */
 
4429
    end_time = os_get_sys_clock_ms() + timeout;
 
4430
    
 
4431
    /* use the default window for input */
 
4432
    if ((win = S_default_win) == 0)
 
4433
        return OS_EVT_EOF;
 
4434
 
 
4435
    /* process keystrokes until we're done entering the command */
 
4436
    for ( ;; )
 
4437
    {
 
4438
        int event_type;
 
4439
        os_event_info_t event_info;
 
4440
        
 
4441
        /* if we're using a timeout, check for expiration */
 
4442
        if (use_timeout)
 
4443
        {
 
4444
            long cur_clock;
 
4445
 
 
4446
            /* note the current system clock time */
 
4447
            cur_clock = os_get_sys_clock_ms();
 
4448
 
 
4449
            /* 
 
4450
             *   if we're past the timeout expiration time already,
 
4451
             *   interrupt with timeout now 
 
4452
             */
 
4453
            if (cur_clock >= end_time)
 
4454
                goto timeout_expired;
 
4455
 
 
4456
            /* note the interval remaining to the timeout expiration */
 
4457
            timeout = end_time - cur_clock;
 
4458
        }
 
4459
        
 
4460
        /* move to the proper position on the screen before pausing */
 
4461
        if (S_sbmode_win != 0)
 
4462
            ossloc(S_sbmode_win->base.winy, S_sbmode_win->base.winx);
 
4463
        else
 
4464
            ossloc(S_gets_y, S_gets_x);
 
4465
 
 
4466
        /* get an event */
 
4467
        event_type = os_get_event(timeout, use_timeout, &event_info);
 
4468
 
 
4469
        /* handle the event according to the event type */
 
4470
        switch(event_type)
 
4471
        {
 
4472
        case OS_EVT_TIMEOUT:
 
4473
        timeout_expired:
 
4474
            /* done with the overridden cursor position */
 
4475
            S_special_cursor_pos = FALSE;
 
4476
 
 
4477
            /* return the timeout status to the caller */
 
4478
            return OS_EVT_TIMEOUT;
 
4479
 
 
4480
        case OS_EVT_NOTIMEOUT:
 
4481
            /* 
 
4482
             *   we can't handle events with timeouts, so we can't provide
 
4483
             *   line reading with timeouts, either
 
4484
             */
 
4485
            S_special_cursor_pos = FALSE;
 
4486
            return OS_EVT_NOTIMEOUT;
 
4487
 
 
4488
        case OS_EVT_EOF:
 
4489
            /* end of file - end input and return the EOF to our caller */
 
4490
            S_special_cursor_pos = FALSE;
 
4491
            S_gets_in_progress = FALSE;
 
4492
            return OS_EVT_EOF;
 
4493
 
 
4494
        default:
 
4495
            /* process anything else through the input line editor */
 
4496
            if (os_gets_process(event_type, &event_info))
 
4497
            {
 
4498
                /* 
 
4499
                 *   Copy the result to the caller's buffer.  Note that we
 
4500
                 *   know the result will fit, because we always limit the
 
4501
                 *   editing process to the caller's buffer size.  
 
4502
                 */
 
4503
                strcpy((char *)buf, S_gets_buf);
 
4504
 
 
4505
                /* clear the input buffer */
 
4506
                S_gets_buf[0] = '\0';
 
4507
 
 
4508
                /* input is no longer in progress */
 
4509
                S_gets_in_progress = FALSE;
 
4510
 
 
4511
                /* return success */
 
4512
                return OS_EVT_LINE;
 
4513
            }
 
4514
            break;
 
4515
        }
 
4516
    }
 
4517
}
 
4518
 
 
4519
/*
 
4520
 *   Read a line of input.  We implement this in terms of the timeout input
 
4521
 *   line reader, passing an infinite timeout to that routine.
 
4522
 */
 
4523
uchar *os_gets(unsigned char *buf, size_t bufl)
 
4524
{
 
4525
    int evt;
 
4526
 
 
4527
    /* cancel any previous input, clearing the buffer */
 
4528
    os_gets_cancel(TRUE);
 
4529
 
 
4530
    /* get a line of input, with no timeout */
 
4531
    evt = os_gets_timeout(buf, bufl, 0, FALSE);
 
4532
 
 
4533
    /* translate the event code to the appropriate return value */
 
4534
    switch(evt)
 
4535
    {
 
4536
    case OS_EVT_LINE:
 
4537
        /* we got a line of input - return a pointer to our buffer */
 
4538
        return buf;
 
4539
 
 
4540
    case OS_EVT_EOF:
 
4541
        /* end of file - return null */
 
4542
        return 0;
 
4543
 
 
4544
    default:
 
4545
        /* we don't expect any other results */
 
4546
        assert(FALSE);
 
4547
        return 0;
 
4548
    }
 
4549
}
 
4550
 
 
4551
#else /* USE_STATLINE */
 
4552
 
 
4553
#endif /* USE_STATLINE */
 
4554
 
 
4555
/* ------------------------------------------------------------------------ */
 
4556
/*
 
4557
 *   Highlighting and colors 
 
4558
 */
 
4559
 
 
4560
#ifdef STD_OS_HILITE
 
4561
 
 
4562
#ifdef RUNTIME
 
4563
/*
 
4564
 *   Set text attributes 
 
4565
 */
 
4566
void os_set_text_attr(int attr)
 
4567
{
 
4568
    osgen_txtwin_t *win;
 
4569
 
 
4570
    /* if there's no default output window, do nothing */
 
4571
    if ((win = S_default_win) == 0)
 
4572
        return;
 
4573
 
 
4574
    /* 
 
4575
     *   if the attributes are different from the old attributes, add an
 
4576
     *   attribute-change sequence to the display buffer 
 
4577
     */
 
4578
    if (attr != win->base.txtattr)
 
4579
    {
 
4580
        char buf[3];
 
4581
 
 
4582
        /* set up the attribute-change sequence */
 
4583
        buf[0] = OSGEN_ATTR;
 
4584
        buf[1] = (char)attr;
 
4585
        ossaddsb(win, buf, 2, TRUE);
 
4586
    }
 
4587
}
 
4588
 
 
4589
/*
 
4590
 *   Translate a color from the os_color_t encoding to an OSGEN_xxx color.  
 
4591
 */
 
4592
static char osgen_xlat_color_t(os_color_t color)
 
4593
{
 
4594
    size_t i;
 
4595
    struct color_map_t
 
4596
    {
 
4597
        /* the OSGEN_COLOR_xxx value */
 
4598
        char id;
 
4599
 
 
4600
        /* the RGB components for the color */
 
4601
        unsigned char rgb[3];
 
4602
    };
 
4603
    struct color_map_t *p;
 
4604
    struct color_map_t *bestp;
 
4605
    static struct color_map_t color_map[] =
 
4606
    {
 
4607
        { OSGEN_COLOR_BLACK,   { 0x00, 0x00, 0x00 } },
 
4608
        { OSGEN_COLOR_WHITE,   { 0xFF, 0xFF, 0xFF } },
 
4609
        { OSGEN_COLOR_RED,     { 0xFF, 0x00, 0x00 } },
 
4610
        { OSGEN_COLOR_BLUE,    { 0x00, 0x00, 0xFF } },
 
4611
        { OSGEN_COLOR_GREEN,   { 0x00, 0x80, 0x00 } },
 
4612
        { OSGEN_COLOR_YELLOW,  { 0xFF, 0xFF, 0x00 } },
 
4613
        { OSGEN_COLOR_CYAN,    { 0x00, 0xFF, 0xFF } },
 
4614
        { OSGEN_COLOR_SILVER,  { 0xC0, 0xC0, 0xC0 } },
 
4615
        { OSGEN_COLOR_GRAY,    { 0x80, 0x80, 0x80 } },
 
4616
        { OSGEN_COLOR_MAROON,  { 0x80, 0x00, 0x00 } },
 
4617
        { OSGEN_COLOR_PURPLE,  { 0x80, 0x00, 0x80 } },
 
4618
        { OSGEN_COLOR_MAGENTA, { 0xFF, 0x00, 0xFF } },
 
4619
        { OSGEN_COLOR_LIME,    { 0x00, 0xFF, 0x00 } },
 
4620
        { OSGEN_COLOR_OLIVE,   { 0x80, 0x80, 0x00 } },
 
4621
        { OSGEN_COLOR_NAVY,    { 0x00, 0x00, 0x80 } },
 
4622
        { OSGEN_COLOR_TEAL,    { 0x00, 0x80, 0x80 } }
 
4623
    };
 
4624
    unsigned char r, g, b;
 
4625
    unsigned long best_dist;
 
4626
 
 
4627
    /* 
 
4628
     *   If it's parameterized, map it by shifting the parameter code (in
 
4629
     *   the high-order 8 bits of the os_color_t) to our single-byte code,
 
4630
     *   which is defined as exactly the same code as the os_color_t values
 
4631
     *   but shifted into the low-order 8 bits.  
 
4632
     */
 
4633
    if (os_color_is_param(color))
 
4634
        return (char)((color >> 24) & 0xFF);
 
4635
 
 
4636
    /* break the color into its components */
 
4637
    r = os_color_get_r(color);
 
4638
    g = os_color_get_g(color);
 
4639
    b = os_color_get_b(color);
 
4640
 
 
4641
    /* search for the closest match among our 16 ANSI colors */
 
4642
    for (i = 0, p = color_map, bestp = 0, best_dist = 0xFFFFFFFF ;
 
4643
         i < sizeof(color_map)/sizeof(color_map[0]) ; ++i, ++p)
 
4644
    {
 
4645
        unsigned long dist;
 
4646
        int rd, gd, bd;
 
4647
 
 
4648
        /* calculate the delta for each component */
 
4649
        rd = r - p->rgb[0];
 
4650
        gd = g - p->rgb[1];
 
4651
        bd = b - p->rgb[2];
 
4652
 
 
4653
        /* calculate the "distance" in RGB space */
 
4654
        dist = rd*rd + gd*gd + bd*bd;
 
4655
 
 
4656
        /* if it's an exact match, we need look no further */
 
4657
        if (dist == 0)
 
4658
            return p->id;
 
4659
 
 
4660
        /* if it's the smallest distance so far, note it */
 
4661
        if (dist < best_dist)
 
4662
        {
 
4663
            best_dist = dist;
 
4664
            bestp = p;
 
4665
        }
 
4666
    }
 
4667
 
 
4668
    /* return the OSGEN_COLOR_xxx ID of the best match we found */
 
4669
    return bestp->id;
 
4670
}
 
4671
 
 
4672
/*
 
4673
 *   Set the text colors.
 
4674
 *   
 
4675
 *   The foreground and background colors apply to subsequent characters
 
4676
 *   displayed via os_print().  If the background color is set to zero, it
 
4677
 *   indicates "transparent" drawing: subsequent text is displayed with the
 
4678
 *   "screen" color.  
 
4679
 */
 
4680
void os_set_text_color(os_color_t fg, os_color_t bg)
 
4681
{
 
4682
    char buf[4];
 
4683
 
 
4684
    /* if we're in plain mode, ignore it */
 
4685
    if (os_f_plain || S_default_win == 0)
 
4686
        return;
 
4687
 
 
4688
    /* add the color sequence to the default window's scrollback buffer */
 
4689
    buf[0] = OSGEN_COLOR;
 
4690
    buf[1] = osgen_xlat_color_t(fg);
 
4691
    buf[2] = osgen_xlat_color_t(bg);
 
4692
    ossaddsb(S_default_win, buf, 3, TRUE);
 
4693
}
 
4694
 
 
4695
/*
 
4696
 *   Set the screen color 
 
4697
 */
 
4698
void os_set_screen_color(os_color_t color)
 
4699
{
 
4700
    /* if we're in plain mode, ignore it */
 
4701
    if (os_f_plain || S_default_win == 0)
 
4702
        return;
 
4703
 
 
4704
    /* set the new background color in the default buffer */
 
4705
    S_default_win->base.fillcolor = osgen_xlat_color_t(color);
 
4706
    S_default_win->base.oss_fillcolor =
 
4707
        ossgetcolor(OSGEN_COLOR_TEXT, osgen_xlat_color_t(color), 0, 0);
 
4708
 
 
4709
    /* redraw the window if we don't have a scheduled redraw already */
 
4710
    if (!S_deferred_redraw
 
4711
        && !(S_default_win->base.flags & OSGEN_DEFER_REDRAW))
 
4712
        osgen_redraw_win(&S_default_win->base);
 
4713
}
 
4714
 
 
4715
/* ------------------------------------------------------------------------ */
 
4716
/*
 
4717
 *   Banners 
 
4718
 */
 
4719
 
 
4720
/*
 
4721
 *   create a banner window 
 
4722
 */
 
4723
void *os_banner_create(void *parent, int where, void *other, int wintype,
 
4724
                       int align, int siz, int siz_units, unsigned long style)
 
4725
{
 
4726
    osgen_win_t *win;
 
4727
 
 
4728
    /* we don't support banners in plain mode */
 
4729
    if (os_f_plain)
 
4730
        return 0;
 
4731
 
 
4732
    /* if the parent is null, it means that it's a child of the main window */
 
4733
    if (parent == 0)
 
4734
        parent = &S_main_win->base;
 
4735
 
 
4736
    /* check for a supported window type */
 
4737
    switch(wintype)
 
4738
    {
 
4739
    case OS_BANNER_TYPE_TEXT:
 
4740
        /* 
 
4741
         *   Create a text window.  We don't support scrollback in the UI in
 
4742
         *   banner windows, so we only need enough for what's on the screen
 
4743
         *   for redrawing.  Overallocate by a bit, though, to be safe in
 
4744
         *   case the screen grows later.  
 
4745
         */
 
4746
        win = (osgen_win_t *)osgen_create_txtwin(where, other, parent,
 
4747
            G_oss_screen_height * G_oss_screen_width * 2,
 
4748
            G_oss_screen_height * 2);
 
4749
        break;
 
4750
 
 
4751
    case OS_BANNER_TYPE_TEXTGRID:
 
4752
        /*
 
4753
         *   Create a text grid window.  Make it ten lines high at the
 
4754
         *   current screen width; we'll automatically expand this
 
4755
         *   allocation as needed later, so this size doesn't have to be a
 
4756
         *   perfect guess; but the closer we get the better, as it's more
 
4757
         *   efficient to avoid reallocating if possible.  
 
4758
         */
 
4759
        win = (osgen_win_t *)osgen_create_gridwin(where, other, parent,
 
4760
            G_oss_screen_width, 10);
 
4761
        break;
 
4762
 
 
4763
    default:
 
4764
        /* unsupported type - return failure */
 
4765
        return 0;
 
4766
    }
 
4767
    
 
4768
    /* if that failed, return null */
 
4769
    if (win == 0)
 
4770
        return 0;
 
4771
 
 
4772
    /* set the alignment */
 
4773
    win->alignment = align;
 
4774
 
 
4775
    /*
 
4776
     *   Start out width a zero size in the settable dimension, and the
 
4777
     *   current main text area size in the constrained dimension. 
 
4778
     */
 
4779
    if (align == OS_BANNER_ALIGN_LEFT || align == OS_BANNER_ALIGN_RIGHT)
 
4780
    {
 
4781
        /* the width is the settable dimension for a left/right banner */
 
4782
        win->wid = 0;
 
4783
        win->ht = (S_main_win != 0
 
4784
                   ? S_main_win->base.ht : G_oss_screen_height);
 
4785
    }
 
4786
    else
 
4787
    {
 
4788
        /* the height is the settable dimension for a top/bottom banner */
 
4789
        win->wid = (S_main_win != 0
 
4790
                    ? S_main_win->base.wid : G_oss_screen_width);
 
4791
        win->ht = 0;
 
4792
    }
 
4793
 
 
4794
    /* set auto-vscroll mode if they want it */
 
4795
    if ((style & OS_BANNER_STYLE_AUTO_VSCROLL) != 0)
 
4796
        win->flags |= OSGEN_AUTO_VSCROLL;
 
4797
 
 
4798
    /* 
 
4799
     *   Note the MORE mode style, if specified.  MORE mode implies
 
4800
     *   auto-vscroll, so add that style as well if MORE mode is requested.  
 
4801
     */
 
4802
    if ((style & OS_BANNER_STYLE_MOREMODE) != 0)
 
4803
        win->flags |= OSGEN_AUTO_VSCROLL | OSGEN_MOREMODE;
 
4804
 
 
4805
    /* note the "strut" style flags, if specified */
 
4806
    if ((style & OS_BANNER_STYLE_VSTRUT) != 0)
 
4807
        win->flags |= OSGEN_VSTRUT;
 
4808
    if ((style & OS_BANNER_STYLE_HSTRUT) != 0)
 
4809
        win->flags |= OSGEN_HSTRUT;
 
4810
 
 
4811
    /* remember the requested size */
 
4812
    win->size = siz;
 
4813
    win->size_type = siz_units;
 
4814
 
 
4815
    /* 
 
4816
     *   if the window has a non-zero size, recalculate the layout; if the
 
4817
     *   size is zero, we don't have to bother, since the layout won't affect
 
4818
     *   anything on the display 
 
4819
     */
 
4820
    if (siz != 0)
 
4821
        osgen_recalc_layout();
 
4822
 
 
4823
    /* return the window */
 
4824
    return win;
 
4825
}
 
4826
 
 
4827
/*
 
4828
 *   delete a banner 
 
4829
 */
 
4830
void os_banner_delete(void *banner_handle)
 
4831
{
 
4832
    osgen_win_t *win = (osgen_win_t *)banner_handle;
 
4833
 
 
4834
    /* delete the window */
 
4835
    osgen_delete_win(win);
 
4836
 
 
4837
    /* recalculate the display layout */
 
4838
    osgen_recalc_layout();
 
4839
}
 
4840
 
 
4841
/*
 
4842
 *   orphan a banner - treat this exactly like delete 
 
4843
 */
 
4844
void os_banner_orphan(void *banner_handle)
 
4845
{
 
4846
    os_banner_delete(banner_handle);
 
4847
}
 
4848
 
 
4849
/*
 
4850
 *   get information on the banner 
 
4851
 */
 
4852
int os_banner_getinfo(void *banner_handle, os_banner_info_t *info)
 
4853
{
 
4854
    osgen_win_t *win = (osgen_win_t *)banner_handle;
 
4855
 
 
4856
    /* set the alignment */
 
4857
    info->align = win->alignment;
 
4858
 
 
4859
    /* set the flags */
 
4860
    info->style = 0;
 
4861
    if ((win->flags & OSGEN_AUTO_VSCROLL) != 0)
 
4862
        info->style |= OS_BANNER_STYLE_AUTO_VSCROLL;
 
4863
    if ((win->flags & OSGEN_MOREMODE) != 0)
 
4864
        info->style |= OS_BANNER_STYLE_MOREMODE;
 
4865
    if ((win->flags & OSGEN_HSTRUT) != 0)
 
4866
        info->style |= OS_BANNER_STYLE_HSTRUT;
 
4867
    if ((win->flags & OSGEN_VSTRUT) != 0)
 
4868
        info->style |= OS_BANNER_STYLE_VSTRUT;
 
4869
 
 
4870
    /* set the character size */
 
4871
    info->rows = win->ht;
 
4872
    info->columns = win->wid;
 
4873
 
 
4874
    /* we're a character-mode platform, so we don't have a pixel size */
 
4875
    info->pix_width = 0;
 
4876
    info->pix_height = 0;
 
4877
 
 
4878
    /* 
 
4879
     *   We are designed for fixed-pitch character-mode displays only, so we
 
4880
     *   support <TAB> alignment by virtue of our fixed pitch.
 
4881
     */
 
4882
    info->style |= OS_BANNER_STYLE_TAB_ALIGN;
 
4883
 
 
4884
    /* we do not do our own line wrapping */
 
4885
    info->os_line_wrap = FALSE;
 
4886
 
 
4887
    /* indicate success */
 
4888
    return TRUE;
 
4889
}
 
4890
 
 
4891
/*
 
4892
 *   clear the contents of a banner 
 
4893
 */
 
4894
void os_banner_clear(void *banner_handle)
 
4895
{
 
4896
    osgen_win_t *win = (osgen_win_t *)banner_handle;
 
4897
 
 
4898
    /* clear the window */
 
4899
    osgen_clear_win(win);
 
4900
}
 
4901
 
 
4902
/*
 
4903
 *   display text in a banner 
 
4904
 */
 
4905
void os_banner_disp(void *banner_handle, const char *txt, size_t len)
 
4906
{
 
4907
    osgen_win_t *win = (osgen_win_t *)banner_handle;
 
4908
 
 
4909
    /* write the text according to the window type */
 
4910
    switch(win->win_type)
 
4911
    {
 
4912
    case OS_BANNER_TYPE_TEXT:
 
4913
        /* normal text window - write the text into the scrollback buffer */
 
4914
        ossaddsb((osgen_txtwin_t *)win, txt, len, TRUE);
 
4915
        break;
 
4916
 
 
4917
    case OS_BANNER_TYPE_TEXTGRID:
 
4918
        /* text grid - write the text into the grid */
 
4919
        osgen_gridwin_write((osgen_gridwin_t *)win, txt, len);
 
4920
        break;
 
4921
    }
 
4922
}
 
4923
 
 
4924
/*
 
4925
 *   set the text attributes in a banner 
 
4926
 */
 
4927
void os_banner_set_attr(void *banner_handle, int attr)
 
4928
{
 
4929
    osgen_win_t *win = (osgen_win_t *)banner_handle;
 
4930
    char buf[3];
 
4931
 
 
4932
    /* if the attributes aren't changing, ignore it */
 
4933
    if (attr == win->txtattr)
 
4934
        return;
 
4935
 
 
4936
    /* set the attribute according to the window type */
 
4937
    switch(win->win_type)
 
4938
    {
 
4939
    case OS_BANNER_TYPE_TEXT:
 
4940
        /* add the color sequence to the window's scrollback buffer */
 
4941
        buf[0] = OSGEN_ATTR;
 
4942
        buf[1] = (char)attr;
 
4943
        ossaddsb((osgen_txtwin_t *)win, buf, 2, TRUE);
 
4944
        break;
 
4945
 
 
4946
    case OS_BANNER_TYPE_TEXTGRID:
 
4947
        /* text grid windows don't use attributes - ignore it */
 
4948
        break;
 
4949
    }
 
4950
}
 
4951
 
 
4952
/*
 
4953
 *   set the text color in a banner
 
4954
 */
 
4955
void os_banner_set_color(void *banner_handle, os_color_t fg, os_color_t bg)
 
4956
{
 
4957
    osgen_win_t *win = (osgen_win_t *)banner_handle;
 
4958
    char buf[4];
 
4959
 
 
4960
    /* set the color according to the window type */
 
4961
    switch(win->win_type)
 
4962
    {
 
4963
    case OS_BANNER_TYPE_TEXT:
 
4964
        /* add the color sequence to the window's scrollback buffer */
 
4965
        buf[0] = OSGEN_COLOR;
 
4966
        buf[1] = osgen_xlat_color_t(fg);
 
4967
        buf[2] = osgen_xlat_color_t(bg);
 
4968
        ossaddsb((osgen_txtwin_t *)win, buf, 3, TRUE);
 
4969
        break;
 
4970
 
 
4971
    case OS_BANNER_TYPE_TEXTGRID:
 
4972
        /* simply set the current color in the window */
 
4973
        win->txtfg = osgen_xlat_color_t(fg);
 
4974
        win->txtbg = osgen_xlat_color_t(bg);
 
4975
        break;
 
4976
    }
 
4977
}
 
4978
 
 
4979
/*
 
4980
 *   set the window color in a banner 
 
4981
 */
 
4982
void os_banner_set_screen_color(void *banner_handle, os_color_t color)
 
4983
{
 
4984
    osgen_win_t *win = (osgen_win_t *)banner_handle;
 
4985
 
 
4986
    /* set the new background color in the window */
 
4987
    win->fillcolor = osgen_xlat_color_t(color);
 
4988
    win->oss_fillcolor = ossgetcolor(OSGEN_COLOR_TEXT, win->fillcolor, 0, 0);
 
4989
 
 
4990
    /* redraw the window if we don't have a redraw scheduled already */
 
4991
    if (!S_deferred_redraw && !(win->flags & OSGEN_DEFER_REDRAW))
 
4992
        osgen_redraw_win(win);
 
4993
}
 
4994
 
 
4995
/*
 
4996
 *   flush text in a banner 
 
4997
 */
 
4998
void os_banner_flush(void *banner_handle)
 
4999
{
 
5000
    osgen_win_t *win = (osgen_win_t *)banner_handle;
 
5001
 
 
5002
    /* if we're deferring redrawing, redraw now */
 
5003
    osgen_redraw_win_if_needed(FALSE, win);
 
5004
}
 
5005
 
 
5006
/*
 
5007
 *   set the size 
 
5008
 */
 
5009
void os_banner_set_size(void *banner_handle, int siz, int siz_units,
 
5010
                        int is_advisory)
 
5011
{
 
5012
    osgen_win_t *win = (osgen_win_t *)banner_handle;
 
5013
 
 
5014
    /* if the size isn't changing, do nothing */
 
5015
    if (win->size == siz && win->size_type == siz_units)
 
5016
        return;
 
5017
 
 
5018
    /* 
 
5019
     *   if the size is only advisory, ignore it, since we do implement
 
5020
     *   size-to-contents 
 
5021
     */
 
5022
    if (is_advisory)
 
5023
        return;
 
5024
 
 
5025
    /* set the new size */
 
5026
    win->size = siz;
 
5027
    win->size_type = siz_units;
 
5028
 
 
5029
    /* recalculate the layout */
 
5030
    osgen_recalc_layout();
 
5031
}
 
5032
 
 
5033
/*
 
5034
 *   calculate the content height, for os_banner_size_to_contents()
 
5035
 */
 
5036
static size_t oss_get_content_height(osgen_win_t *win)
 
5037
{
 
5038
    size_t y;
 
5039
    osgen_win_t *chi;
 
5040
    
 
5041
    /* start with our own maximum 'y' size */
 
5042
    y = win->ymax + 1;
 
5043
 
 
5044
    /* scan children for vertical strut styles, and include any we find */
 
5045
    for (chi = win->first_child ; chi != 0 ; chi = chi->nxt)
 
5046
    {
 
5047
        /* if this is a vertical strut, include its height as well */
 
5048
        if ((chi->flags & OSGEN_VSTRUT) != 0)
 
5049
        {
 
5050
            /* calculate the child height */
 
5051
            size_t chi_y = oss_get_content_height(chi);
 
5052
 
 
5053
            /* 
 
5054
             *   if the child is horizontal, add its height to the parent's;
 
5055
             *   otherwise, it shares the same height, so use the larger of
 
5056
             *   the parent's natural height or the child's natural height 
 
5057
             */
 
5058
            if (chi->alignment == OS_BANNER_ALIGN_TOP
 
5059
                || chi->alignment == OS_BANNER_ALIGN_BOTTOM)
 
5060
            {
 
5061
                /* it's horizontal - add its height to the parent's */
 
5062
                y += chi_y;
 
5063
            }
 
5064
            else
 
5065
            {
 
5066
                /* it's vertical - they share a common height */
 
5067
                if (y < chi_y)
 
5068
                    y = chi_y;
 
5069
            }
 
5070
        }
 
5071
    }
 
5072
 
 
5073
    /* return the result */
 
5074
    return y;
 
5075
}
 
5076
 
 
5077
/*
 
5078
 *   calculate the content width, for os_banner_size_to_contents() 
 
5079
 */
 
5080
static size_t oss_get_content_width(osgen_win_t *win)
 
5081
{
 
5082
    size_t x;
 
5083
    osgen_win_t *chi;
 
5084
    
 
5085
    /* start with the maximum 'x' size we've seen in the window */
 
5086
    x = win->xmax + 1;
 
5087
 
 
5088
    /* scan children for horizontal strut styles, and include any we find */
 
5089
    for (chi = win->first_child ; chi != 0 ; chi = chi->nxt)
 
5090
    {
 
5091
        /* if this is a horizontal strut, include its height as well */
 
5092
        if ((chi->flags & OSGEN_HSTRUT) != 0)
 
5093
        {
 
5094
            /* calculate the child width */
 
5095
            size_t chi_x = oss_get_content_width(chi);
 
5096
 
 
5097
            /* 
 
5098
             *   if the child is vertical, add its width to the parent's;
 
5099
             *   otherwise, it shares the same width, so use the larger of
 
5100
             *   the parent's natural width or the child's natural width 
 
5101
             */
 
5102
            if (chi->alignment == OS_BANNER_ALIGN_LEFT
 
5103
                || chi->alignment == OS_BANNER_ALIGN_RIGHT)
 
5104
            {
 
5105
                /* it's vertical - add its width to the parent's */
 
5106
                x += chi_x;
 
5107
            }
 
5108
            else
 
5109
            {
 
5110
                /* it's horizontal - they share a common width */
 
5111
                if (x < chi_x)
 
5112
                    x = chi_x;
 
5113
            }
 
5114
        }
 
5115
    }
 
5116
 
 
5117
    /* return the result */
 
5118
    return x;
 
5119
}
 
5120
 
 
5121
/*
 
5122
 *   size a banner to its contents 
 
5123
 */
 
5124
void os_banner_size_to_contents(void *banner_handle)
 
5125
{
 
5126
    osgen_win_t *win = (osgen_win_t *)banner_handle;
 
5127
 
 
5128
    /* the sizing depends on the window's alignment */
 
5129
    if (win->alignment == OS_BANNER_ALIGN_TOP
 
5130
        || win->alignment == OS_BANNER_ALIGN_BOTTOM)
 
5131
    {
 
5132
        size_t newy;
 
5133
 
 
5134
        /* calculate the new height */
 
5135
        newy = oss_get_content_height(win);
 
5136
 
 
5137
        /* 
 
5138
         *   if this is the same as the current height, there's no need to
 
5139
         *   redraw anything 
 
5140
         */
 
5141
        if (win->ht == newy)
 
5142
            return;
 
5143
 
 
5144
        /* set the new size as a fixed character-cell size */
 
5145
        win->size = newy;
 
5146
        win->size_type = OS_BANNER_SIZE_ABS;
 
5147
    }
 
5148
    else
 
5149
    {
 
5150
        size_t newx;
 
5151
 
 
5152
        /* calculate the new width */
 
5153
        newx = oss_get_content_width(win);
 
5154
 
 
5155
        /* if the size isn't changing, there's no need to redraw */
 
5156
        if (win->wid == newx)
 
5157
            return;
 
5158
 
 
5159
        /* set the new size as a fixed character-cell size */
 
5160
        win->size = newx;
 
5161
        win->size_type = OS_BANNER_SIZE_ABS;
 
5162
    }
 
5163
 
 
5164
    /* recalculate the window layout */
 
5165
    osgen_recalc_layout();
 
5166
}
 
5167
 
 
5168
/*
 
5169
 *   get the width, in characters, of the banner window 
 
5170
 */
 
5171
int os_banner_get_charwidth(void *banner_handle)
 
5172
{
 
5173
    /* return the current width from the window */
 
5174
    return ((osgen_win_t *)banner_handle)->wid;
 
5175
}
 
5176
 
 
5177
/*
 
5178
 *   get the height, in characters, of the banner window 
 
5179
 */
 
5180
int os_banner_get_charheight(void *banner_handle)
 
5181
{
 
5182
    /* return the current height from the window */
 
5183
    return ((osgen_win_t *)banner_handle)->ht;
 
5184
}
 
5185
 
 
5186
/*
 
5187
 *   start HTML mode in a banner 
 
5188
 */
 
5189
void os_banner_start_html(void *banner_handle)
 
5190
{
 
5191
    /* we don't support HTML mode, so there's nothing to do */
 
5192
}
 
5193
 
 
5194
/*
 
5195
 *   end HTML mode in a banner 
 
5196
 */
 
5197
void os_banner_end_html(void *banner_handle)
 
5198
{
 
5199
    /* we don't support HTML mode, so there's nothing to do */
 
5200
}
 
5201
 
 
5202
/*
 
5203
 *   set the output position in a text grid window 
 
5204
 */
 
5205
void os_banner_goto(void *banner_handle, int row, int col)
 
5206
{
 
5207
    osgen_win_t *win = (osgen_win_t *)banner_handle;
 
5208
 
 
5209
    /* check the window type */
 
5210
    switch(win->win_type)
 
5211
    {
 
5212
    case OS_BANNER_TYPE_TEXTGRID:
 
5213
        /* it's a text grid - move the output position */
 
5214
        win->y = row;
 
5215
        win->x = col;
 
5216
        break;
 
5217
 
 
5218
    default:
 
5219
        /* 
 
5220
         *   this operation is meaningless with other window types - simply
 
5221
         *   ignore it 
 
5222
         */
 
5223
        break;
 
5224
    }
 
5225
}
 
5226
 
 
5227
/* ------------------------------------------------------------------------ */
 
5228
/*
 
5229
 *   Non-RUNTIME version 
 
5230
 */
 
5231
#else /* RUNTIME */
 
5232
 
 
5233
void os_set_text_attr(int attr)
 
5234
{
 
5235
    /* attributes aren't supported in non-RUNTIME mode - ignore it */
 
5236
}
 
5237
 
 
5238
void os_set_text_color(os_color_t fg, os_color_t bg)
 
5239
{
 
5240
    /* colors aren't supported in non-RUNTIME mode - ignore it */
 
5241
}
 
5242
 
 
5243
void os_set_screen_color(os_color_t color)
 
5244
{
 
5245
    /* colors aren't supported in non-RUNTIME mode - ignore it */
 
5246
}
 
5247
 
 
5248
/*
 
5249
 *   Banners aren't supported in plain mode 
 
5250
 */
 
5251
 
 
5252
void *os_banner_create(void *parent, int where, void *other, int wintype,
 
5253
                       int align, int siz, int siz_units, unsigned long style)
 
5254
{
 
5255
    return 0;
 
5256
}
 
5257
 
 
5258
void os_banner_delete(void *banner_handle)
 
5259
{
 
5260
}
 
5261
 
 
5262
void os_banner_orphan(void *banner_handle)
 
5263
{
 
5264
}
 
5265
 
 
5266
int os_banner_getinfo(void *banner_handle, os_banner_info_t *info)
 
5267
{
 
5268
    return FALSE;
 
5269
}
 
5270
 
 
5271
void os_banner_clear(void *banner_handle)
 
5272
{
 
5273
}
 
5274
 
 
5275
int os_banner_get_charwidth(void *banner_handle)
 
5276
{
 
5277
    return 0;
 
5278
}
 
5279
 
 
5280
int os_banner_get_charheight(void *banner_handle)
 
5281
{
 
5282
    return 0;
 
5283
}
 
5284
 
 
5285
void os_banner_disp(void *banner_handle, const char *txt, size_t len)
 
5286
{
 
5287
}
 
5288
 
 
5289
void os_banner_set_color(void *banner_handle, os_color_t fg, os_color_t bg)
 
5290
{
 
5291
}
 
5292
 
 
5293
void os_banner_set_screen_color(void *banner_handle, os_color_t color)
 
5294
{
 
5295
}
 
5296
 
 
5297
void os_banner_flush(void *banner_handle)
 
5298
{
 
5299
}
 
5300
 
 
5301
void os_banner_set_size(void *banner_handle, int siz, int siz_units,
 
5302
                        int is_advisory)
 
5303
{
 
5304
}
 
5305
 
 
5306
void os_banner_size_to_contents(void *banner_handle)
 
5307
{
 
5308
}
 
5309
 
 
5310
void os_banner_start_html(void *banner_handle)
 
5311
{
 
5312
}
 
5313
 
 
5314
void os_banner_end_html(void *banner_handle)
 
5315
{
 
5316
}
 
5317
 
 
5318
void os_banner_goto(void *banner_handle, int row, int col)
 
5319
{
 
5320
}
 
5321
 
 
5322
#endif /* RUNTIME */
 
5323
 
 
5324
#endif /* STD_OS_HILITE */
 
5325
 
 
5326
/* ------------------------------------------------------------------------ */
 
5327
/* 
 
5328
 *   clear the screen, deleting all scrollback information
 
5329
 */
 
5330
#ifdef STD_OSCLS
 
5331
 
 
5332
void oscls(void)
 
5333
{
 
5334
#ifdef RUNTIME
 
5335
    /* do nothing in 'plain' mode */
 
5336
    if (os_f_plain || S_default_win == 0)
 
5337
        return;
 
5338
 
 
5339
    /* clear the default window */
 
5340
    osgen_clear_win(&S_default_win->base);
 
5341
#endif
 
5342
}
 
5343
 
 
5344
#endif /* STD_OSCLS */
 
5345
 
 
5346
/* ------------------------------------------------------------------------ */
 
5347
/*
 
5348
 *   Simple implementation of os_get_sysinfo.  This can be used for any
 
5349
 *   non-HTML version of the system, since all sysinfo codes currently
 
5350
 *   pertain to HTML features.  Note that new sysinfo codes may be added
 
5351
 *   in the future which may be relevant to non-html versions, so the
 
5352
 *   sysinfo codes should be checked from time to time to ensure that new
 
5353
 *   codes relevant to this system version are handled correctly here.  
 
5354
 */
 
5355
int os_get_sysinfo(int code, void *param, long *result)
 
5356
{
 
5357
#ifdef RUNTIME
 
5358
    /* if the oss layer recognizes the code, defer to its judgment */
 
5359
    if (oss_get_sysinfo(code, param, result))
 
5360
        return TRUE;
 
5361
#endif
 
5362
 
 
5363
    /* check the type of information they're requesting */
 
5364
    switch(code)
 
5365
    {
 
5366
    case SYSINFO_HTML:
 
5367
    case SYSINFO_JPEG:
 
5368
    case SYSINFO_PNG:
 
5369
    case SYSINFO_WAV:
 
5370
    case SYSINFO_MIDI:
 
5371
    case SYSINFO_WAV_MIDI_OVL:
 
5372
    case SYSINFO_WAV_OVL:
 
5373
    case SYSINFO_MPEG:
 
5374
    case SYSINFO_MPEG1:
 
5375
    case SYSINFO_MPEG2:
 
5376
    case SYSINFO_MPEG3:
 
5377
    case SYSINFO_PREF_IMAGES:
 
5378
    case SYSINFO_PREF_SOUNDS:
 
5379
    case SYSINFO_PREF_MUSIC:
 
5380
    case SYSINFO_PREF_LINKS:
 
5381
    case SYSINFO_LINKS_HTTP:
 
5382
    case SYSINFO_LINKS_FTP:
 
5383
    case SYSINFO_LINKS_NEWS:
 
5384
    case SYSINFO_LINKS_MAILTO:
 
5385
    case SYSINFO_LINKS_TELNET:
 
5386
    case SYSINFO_PNG_TRANS:
 
5387
    case SYSINFO_PNG_ALPHA:
 
5388
    case SYSINFO_OGG:
 
5389
    case SYSINFO_MNG:
 
5390
    case SYSINFO_MNG_TRANS:
 
5391
    case SYSINFO_MNG_ALPHA:
 
5392
        /* 
 
5393
         *   we don't support any of these features - set the result to 0
 
5394
         *   to indicate this 
 
5395
         */
 
5396
        *result = 0;
 
5397
 
 
5398
        /* return true to indicate that we recognized the code */
 
5399
        return TRUE;
 
5400
 
 
5401
    case SYSINFO_INTERP_CLASS:
 
5402
        /* we're a text-only character-mode interpreter */
 
5403
        *result = SYSINFO_ICLASS_TEXT;
 
5404
        return TRUE;
 
5405
 
 
5406
#ifdef RUNTIME
 
5407
 
 
5408
    case SYSINFO_BANNERS:
 
5409
        /* 
 
5410
         *   we support the os_banner_xxx() interfaces, as long as we're not
 
5411
         *   in "plain" mode 
 
5412
         */
 
5413
        *result = !os_f_plain;
 
5414
        return TRUE;
 
5415
 
 
5416
#endif /* RUNTIME */
 
5417
 
 
5418
    default:
 
5419
        /* not recognized */
 
5420
        return FALSE;
 
5421
    }
 
5422
}
 
5423
 
 
5424
/* ------------------------------------------------------------------------ */
 
5425
/*
 
5426
 *   Set the saved-game extension.  Most platforms don't need to do
 
5427
 *   anything with this information, and in fact most platforms won't even
 
5428
 *   have a way of letting the game author set the saved game extension,
 
5429
 *   so this trivial implementation is suitable for most systems.
 
5430
 *   
 
5431
 *   The purpose of setting a saved game extension is to support platforms
 
5432
 *   (such as Windows) where the filename suffix is used to associate
 
5433
 *   document files with applications.  Each stand-alone executable
 
5434
 *   generated on such platforms must have a unique saved game extension,
 
5435
 *   so that the system can associate each game's saved position files
 
5436
 *   with that game's executable.  
 
5437
 */
 
5438
void os_set_save_ext(const char *ext)
 
5439
{
 
5440
    /* ignore the setting */
 
5441
}
 
5442
 
 
5443
 
 
5444
/* ------------------------------------------------------------------------ */
 
5445
/*
 
5446
 *   Set the game title.  Most platforms have no use for this information,
 
5447
 *   so they'll just ignore it.  This trivial implementation simply
 
5448
 *   ignores the title. 
 
5449
 */
 
5450
#ifdef USE_NULL_SET_TITLE
 
5451
 
 
5452
void os_set_title(const char *title)
 
5453
{
 
5454
    /* ignore the information */
 
5455
}
 
5456
 
 
5457
#endif /* USE_NULL_SET_TITLE */
 
5458