cfad47cfa3/tads2/osgen3.c

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
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