1 /* pMARS -- a portable Memory Array Redcode Simulator
2  * Copyright (C) 1993-1995 Albert Ma, Na'ndor Sieben, Stefan Strack, Mintardjo Wangsawidjaja and Martin Maierhofer
3  * Copyright (C) 2003 M Joonas Pihlaja
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19 #include <string.h>
20 #include <ctype.h>
21 #include <assert.h>
22 #include "SDL.h"
23 
24 #define USE_BLIT_FONTS 1
25 
26 
27 /* ------------------------------------------------------------------------
28  * Types and globals.
29  */
30 
31 /*-- Externs --*/
32 extern char *CDB_PROMPT;
33 extern int curPanel;			/* # current cdb panel: 1 or 2. */
34 extern int curAddr;			/* current core address in cdb. */
35 
36 extern void sighandler(int dummy);	/* Used to tell cdb that the user
37 					   has interrupted a fight. */
38 /* Strings. */
39 extern char *outOfMemory;
40 extern char *errDisplayOpen;
41 extern char *failedSDLInit;
42 extern char *badModeString;
43 extern char *pressAnyKey;
44 extern char *errSpriteConv;
45 extern char *errSpriteColKey;
46 extern char *errSpriteSurf;
47 extern char *errSpriteFill;
48 extern char *errorHeader;
49 
50 
51 /*-- Colours --*/
52 typedef struct RGBColour_st {
53     Uint8 r,g,b;
54 } RGBColour;
55 
56 #define NCOLOURS	16
57 #define L	205			/* low intensity */
58 #define G	211			/* light grey intensity */
59 #define D	190			/* grey intensity */
60 #define H	255			/* high intensity */
61 static const RGBColour PaletteRGB[NCOLOURS] = {
62 #define BLACK 		0
63     { 0, 0, 0 },			/* black */
64     { 0, 0, L },			/* blue3 */
65     { 0, L, 0 },			/* green3 */
66     { 0, L, L },			/* cyan3 */
67 #define RED		4
68     { L, 0, 0 },			/* red3 */
69     { L, 0, L },			/* magenta3 */
70     { L, L, 0 },			/* yellow3 */
71 #define LIGHTGREY	7
72     { G, G, G },			/* light grey */
73 #define GREY            8
74     { D, D, D },			/* grey */
75     { 0, 0, H },			/* blue1 */
76 #define GREEN		10
77     { 0, H, 0 },			/* green1 */
78     { 0, H, H },			/* cyan1 */
79 #define LIGHTRED	12
80     { H, 0, 0 },			/* red1 */
81     { H, 0, H },			/* magenta1 */
82 #define YELLOW		14
83     { H, H, 0 },			/* yellow1 */
84 #define WHITE 15
85     { H, H, H }				/* white */
86 };
87 #undef L
88 #undef G
89 #undef D
90 #undef H
91 
92 static Uint32 Colours[NCOLOURS];	/* Display format colours */
93 					/* that need to be converted. */
94 
95 /*-- The display --*/
96 
97 SDL_Surface *TheSurf;			/* The display surface. */
98 
99 typedef struct VMode_st {		/* Video mode info */
100     Uint16 w;				/* width */
101     Uint16 h;				/* height */
102     Uint16 bpp;				/* bits per pixel */
103     Uint32 flags;			/* flags used to open the surface. */
104 } VMode;
105 
106 VMode TheVMode;				/* Current video mode. */
107 
108 #define NUM_VMODE_FLAGS 5
109 #define DEFAULT_VMODE_FLAGS (SDL_RESIZABLE | SDL_SWSURFACE);
110 #define DEFAULT_VMODE_W 640
111 #define DEFAULT_VMODE_H 480
112 #define DEFAULT_VMODE_BPP 0
113 static struct {
114     const char *name;			/* full name of option */
115     size_t siglen;			/* # significant chars */
116     Uint32 value;			/* bitmask of flags to set or clear. */
117 } const VMode_flags[NUM_VMODE_FLAGS] = {
118     { "fullscreen",	1, SDL_FULLSCREEN },
119     { "resizable",	1, SDL_RESIZABLE },
120     { "any",		1, SDL_ANYFORMAT },
121     { "noframe",	1, SDL_NOFRAME },
122     { "db",		1, SDL_DOUBLEBUF }
123 };
124 
125 
126 /*-- Font --*/
127 
128 /* Font data is held in a NUMCHARS*HEIGHT byte bitmap, where the
129  * bitmap for the Ith character consists of HEIGHT consecutive bytes
130  * at index I*HEIGHT.  The HEIGHT bytes form a 8 by HEIGHT sized
131  * bitmap, where the top left bit of the bitmap is the most
132  * significant bit of the first byte.  For example, the bitmap
133  * for the character `L' in an 8 by 8 font might be:
134  *
135  * byte #\bit # 7 6 5 4 3 2 1 0
136  *   0		0 0 0 0 0 0 0 0
137  *   1		0 1 0 0 0 0 0 0
138  *   2		0 1 0 0 0 0 0 0
139  *   3  	0 1 0 0 0 0 0 0
140  *   4  	0 1 0 0 0 0 0 0
141  *   5  	0 1 0 0 0 0 0 0
142  *   6  	0 1 1 1 1 1 1 0
143  *   7  	0 0 0 0 0 0 0 0
144  *
145  * { 0x00, 0x40, 0x40, 0x40, 0x40, 0x40, 0x7e, 0x00 }
146  * */
147 typedef struct FixedFontInfo_st {
148     const Uint8 *bitmaps;		/* glyph bitmaps */
149     Uint16 h;				/* height */
150     Uint16 w;				/* width, always = 8. */
151     Uint16 nchars;			/* # chars */
152     SDL_Surface *glyphs[NCOLOURS];	/* glyphs bitmaps in native format. */
153 } FixedFontInfo;
154 
155 /* Compile in a default font to use so the user doesn't have to lug around
156  * a font file.  TODO: Let the user choose their own font somehow?
157  **/
158 #include "fnt16.c"			/* Font bitmaps */
159 static FixedFontInfo CurrentFont;
160 
161 
162 /*-- Generic borders --*/
163 
164 typedef struct Border_st {
165     Sint16 lft, rgt, top, bot;
166     /* Width's of borders on the left, right, top and bottom edges.  If
167        negative, no visible border is drawn on the respective edge(s). */
168     int colix;				/* Border colour index. */
169 } Border;
170 
171 static const Border noborder = { 0, 0, 0, 0,  BLACK };
172 
173 
174 /*-- Panels --*/
175 typedef struct Panel_st {
176     SDL_Rect	r;			/* Rectangle of this panel in
177                                            display.  Includes border,
178 					   if the panel has one. */
179     SDL_Rect	v;			/* The visual rectangle (R sans B).*/
180     Border	b;			/* The border. */
181 } Panel;
182 
183 
184 /*-- Generic Panel Layouts --*/
185 
186 typedef int (*layout_func)(Panel *p, void *parms);
187 
188 typedef struct Layout_st {
189     Panel p;				/* Panel for this layout. */
190     Uint16 minw, maxw;			/* Bounds for width and height. */
191     Uint16 minh, maxh;
192 
193     struct Layout_st *parent;		/* Pair layout that contains */
194 					/* this layout, or NULL for a */
195 					/* root layout. */
196 
197     /* The next fields specify call-backs for the user's layouts.  In
198      * the tree of layouts, all callbacks are called in postorder
199      * unless otherwise noted.  */
200     void *parms;			/* Parameters passed to the
201                                            all callbacks. */
202 
203     int (*before_layout)(struct Layout_st *, Uint16, Uint16);
204 	/* Called before proposing a layout with the new width and
205 	   height of the display so that panels may adjust their
206 	   minimum/maximum sizes.  Returns 1: ok to layout, 0: layout
207 	   will fail (display too small). */
208 
209     layout_func	after_layout;
210  	/* Called after the sizes and positions of layed out panels
211 	   have been decided, to give a chance for the user to fail a
212 	   given layout and do other processing.  Returns 1: layout ok,
213 	   0: layout failed. */
214 
215     layout_func doclear;
216         /* Called to clear the layout.  Return value ignored.  Called in
217 	   preorder. */
218 
219     layout_func doredraw;
220     	/* Called when a panel needs to be redrawn.  Return value ignored.
221 	   Called in preorder. */
222 
223     void (*doremode)(void *parms);
224         /* Called after a successfully laying out all layouts and the
225 	   display mode has (possibly) changed.  */
226 
227     void (*doclose)(void *parms);
228         /* Called to close a layout. */
229 
230     layout_func dorefresh;
231         /* Called to refresh a layout.  Return value ignored. */
232 
233     /* The following fields are used only for pair layouts to control
234      * layout splits. */
235     struct Layout_st *a, *b;		/* children. */
236     Uint32 size;			/* percentage of split for child b. */
237     Uint16 method;			/* Method of split. */
238 } Layout;
239 
240 /* When a layout is split we must specify how the new layout is placed in
241    relation to the old layout. */
242 #define BELOWS 0
243 #define ABOVES 1
244 #define LEFTS  2
245 #define RIGHTS 3
246 
247 #define is_horizontal_split(method) (((method) & 2) == 2)
248 
249 #define UNBOUNDED 32767			/* Maximum value of any minimum or
250 					   maximum bound for the height or
251 					   width of a layout. */
252 
253 /* The memory for layout structures is statically allocated, and we provide a
254    simple layout allocator from an array of available layouts.  When in the
255    free-list, layouts are linked together by they `a' field of the Layout
256    structure.  */
257 #define MAXLAYOUTS 40
258 Layout *layout_free_list = NULL;
259 Layout all_layouts[MAXLAYOUTS];
260 
261 /*-- Generic Text Output panels (used for all text panels.) --*/
262 
263 typedef struct TextOutput_st {
264     Layout *layout;
265     Sint16 x, y;			/* Point coordinates. */
266     SDL_Rect refresh;			/* Rectangle that needs refreshing. */
267     SDL_Rect drawn;			/* Rectangle where stuff was drawn. */
268     struct TextOutput_st *next;		/* Next free TextOutput in free list.*/
269 } TextOutput;
270 
271 
272 /* pMARS specific: There must be two more TextOutputs than text inputs
273  * because both the status line and warrior names panels are TextOutputs. */
274 #define MAXTEXTOUTPUTS 22
275 static TextOutput all_textoutputs[MAXTEXTOUTPUTS];
276 static TextOutput *textoutput_free_list = NULL;
277 
278 
279 /*-- Generic Text Input panels (used for cdb text panels.) --*/
280 
281 typedef struct TextInput_st {
282     TextOutput *out;			/* The output panel. */
283     Layout *layout;			/* layout of this text input. */
284     int active;				/* set if accepting input. */
285     int has_focus;			/* set if has focus. */
286     int needs_prompting;		/* set if prompts on activation.*/
287     char prompt[MAXALLCHAR];		/* prompt string. */
288     char buf[MAXALLCHAR];		/* input buffer. */
289     size_t len;				/* input length. */
290     int colix;				/* colour of user input. */
291 
292     struct TextInput_st *next;		/* Next free TextInput in free-list. */
293 } TextInput;
294 
295 /* TextInput structures are also statically allocated and provide a simple
296  * free-list allocator. */
297 #define MAXTEXTINPUTS 10
298 TextInput all_textinputs[MAXTEXTINPUTS];
299 TextInput *textinput_free_list = NULL;
300 
301 
302 /*-- (pMARS specific) --*/
303 
304 static Uint32 WarColourIx[MAXWARRIOR];	/* Colour index (into Colours[]) of */
305 static Uint32 DieColourIx[MAXWARRIOR];  /* warriors and the colours they die
306                                            with.. */
307 
308 /* What core cells look like. */
309 typedef struct CellInfo_st {
310     int		box_size;		/* size of one "pixel" of a cell. */
311     int		interbox_space;		/* # pixels between cell's boxes. */
312     int		intercell_space;	/* # pixels between cells. */
313 } CellInfo;
314 
315 /* The different cell sizes chosen by the pMARS display mode. */
316 static const CellInfo DesiredCells[10] = {
317     {  1,  0,  2 },		/* 0 */
318     {  1,  0,  2 },		/* 1 */
319     {  1,  0,  3 },		/* 2 */
320     {  2,  0,  2 },		/* 3 */
321     {  3,  0,  1 },		/* 4 */
322     {  3,  1,  1 },		/* 5 */
323     {  4,  0,  1 },		/* 6 */
324     {  5,  2,  3 },		/* 7 */
325     { 10,  5,  8 },		/* 8 */
326     { 20, 10, 16 },		/* 9 */
327 };
328 
329 /* The arena (core). */
330 typedef struct Arena_st {
331     Layout		*layout;	/* Layout used for a core. */
332     unsigned char 	*core;		/* For each core cell, four colour
333 					   indices that say what the colour of
334 					   the cell boxes are in the top-left,
335 					   top-right, bottom-right, and
336 					   bottom-left */
337     Uint32		coresize;	/* Guess what? */
338     CellInfo 		cell;		/* The actual cell used that fits
339 					   the core display. */
340     CellInfo		desired_cell;	/* The cell chosen by the current
341 					   pMARS display mode. */
342 
343     SDL_Surface		***blitboxes;
344 	/* For each used colour index (into Colours[]) we precompute sixteen
345 	   sprites, each representing a different way that the top-left,
346 	   top-right, bottom-left and bottom-right boxes of a 2x2 grid can be
347 	   filled with that colour.  During a fight these are then blitted to
348 	   the screen using a potentially fast blit. (For very small core
349 	   cells this is a lot of overhead, but it should pay off for larger
350 	   cells.)  This precomputation is done each time the doremode()
351 	   function of a core layout is called.  */
352 } Arena;
353 
354 /* Bit masks of the top-left, top-right, bottom-left, and bottom-right boxes
355    of a cell. */
356 #define TL 0x00000001
357 #define TR 0x00000002
358 #define BL 0x00000004
359 #define BR 0x00000008
360 
361 static Layout *CoreLayout;		/* The Layout used for the core
362                                            display. */
363 static Arena CoreArena;			/* The Arena used for the core
364                                            display. */
365 int ArenaX, ArenaY;			/* The top-left coordinates of the
366 					   cell at address 0 in core. */
367 int CellSize;				/* # pixels used by a core cell. */
368 int CellsPerLine;			/* # cells that fit in one row. */
369 SDL_Surface ***BlitBoxes;		/* Core cell sprites (same array as in
370 					   CoreArena.blitboxes.) */
371 
372 /* Macros to convert pixel offsets from (ArenaX,ArenaY) into core addresses
373    and vice versa. */
374 #define xkoord(addr) (ArenaX + CellSize*((addr) % CellsPerLine))
375 #define ykoord(addr) (ArenaY + CellSize*((addr) / CellsPerLine))
376 #define addr_of(x,y) (  ((x)-ArenaX)/CellSize \
377 			+ CellsPerLine*((y)-ArenaY)/CellSize )
378 
379 
380 static Layout *StatusLayout = NULL;	/* The Layout used for the status
381                                            line. */
382 
383 static Layout *MetersLayout = NULL;	/* The Layout used for the process and
384 					   cycle meters. */
385 
386 int MetersX;				/* X-coordinate of meters low point. */
387 int splY[MAXWARRIOR];			/* Y-coordinates of process meters. */
388 int cycleY;				/* Y-coordinate of cycle counter. */
389 int processRatio;			/* Ratio of processes to pixels. */
390 int cycleRatio;				/* Ratio of cycles to pixels. */
391 					/* Both ratios are at least 1. */
392 
393 static TextInput *TextPanels[MAXTEXTINPUTS]; /* cdb input panels, indexed by
394 						curPanel-1. */
395 static int NTextPanels = 0;
396 
397 /* These are filled in clparse.c */
398 #define SDLGR_NOPTIONS 1
399 int MaxSDLOptions;
400 const char sdlgr_Options[SDLGR_NOPTIONS] = {
401     'm'					/* The new -m option is used to give
402 					 * the desired screen mode.  */
403 };
404 const char *sdlgr_Storage[SDLGR_NOPTIONS] = {
405     NULL				/* Pointer to argument of -m option */
406 };
407 
408 /* Command History. */
409 
410 typedef struct Cmd_st {
411     char *cmd;				/* The command. */
412     struct Cmd_st *next;		/* earlier command in history. */
413     struct Cmd_st *prev;		/* later command in history. */
414 } Cmd;
415 
416 Cmd CmdRing;				/* List header. */
417 
418 /* ------------------------------------------------------------------------
419  * File-local prototypes.
420  */
421 
422 /* Utilities */
423 static void exit_panic (const char *msg, const char *reason);
424 static void xstrncpy (char *dst, const char *src, size_t maxbuf);
425 static void xstrncat (char *dst, const char *src, size_t maxbuf);
426 static void xstrnapp (char *dst, unsigned int c, size_t maxbuf);
427 static char *xstrdup (const char *s);
428 
429 static void putpixel (SDL_Surface *s, int x, int y, Uint32 col);
430 
431 static void clip_rect (const SDL_Rect *c, SDL_Rect *r);
432 static void join_rect (const SDL_Rect *r, SDL_Rect *c);
433 static void clear_rect (const SDL_Rect *r);
434 
435 /* Display */
436 static const char* decode_vmode_string (VMode *m, const char *str);
437 static void Slock (SDL_Surface *screen);
438 static void Sunlock (SDL_Surface *screen);
439 static SDL_Surface * set_VMode (VMode* m);
440 /* static void clear_display (void); */
441 static void refresh_rect(const SDL_Rect *r);
442 
443 /* Text output primitives. */
444 static void outblankxy (Sint16 x, Sint16 y, int colix, const SDL_Rect *clip);
445 static void outcharxy (unsigned char c, Sint16 x, Sint16 y, int colix,
446 		       const SDL_Rect *clip);
447 static void Font_Init(FixedFontInfo *f, const Uint8* bitmaps,
448 		      Uint16 w, Uint16 h, Uint16 nchars);
449 
450 /* Borders. */
451 static Border border (Sint16 l, Sint16 r, Sint16 t, Sint16 b, int colix);
452 static void draw_border (const SDL_Rect *r, const Border *b);
453 
454 /* Panels */
455 static Panel new_panel (Sint16 x, Sint16 y, Uint16 w, Uint16 h, Border b);
456 static void recompute_panel_view (Panel *p);
457 static const SDL_Rect *panel_view (const Panel *p);
458 static void refresh_panel (const Panel *p);
459 static void panel_text_size (const Panel *p, int *cols, int *lines);
460 static void clear_panel (Panel *p);
461 
462 /* Layouts; general. */
463 static void init_all_layouts (void);
464 static int default_before_layoutfunc (Layout *a, Uint16 w, Uint16 h);
465 static int default_after_layoutfunc (Panel *p, void *parms);
466 static int default_redrawfunc (Panel *p, void *parms);
467 static int default_clearfunc (Panel *p, void *parms);
468 static int default_refreshfunc (Panel *p, void *parms);
469 static void default_closefunc (void *parms);
470 static void default_remodefunc (void *parms);
471 
472 static Layout * alloc_layout (void);
473 static Layout * alloc_pair_layout (void);
474 static void free_layout (Layout *lay);
475 static Uint16 add_bounds (Uint32 a, Uint32 b);
476 /* static Uint16 sub_bounds (Uint32 a, Uint32 b); */
477 static void synthesize_bounds (Layout *pair);
478 static Layout * get_root_layout (Layout *a);
479 static void recursive_free_layout (Layout *a);
480 static Layout * get_sibling_layout (const Layout *a);
481 static void close_layout (Layout *a);
482 static void propose_layout (Layout *pair, Sint16 x, Sint16 y, Uint16 w, Uint16 h);
483 static void redraw_layout_borders (Layout *a);
484 static void clear_layout (Layout *a);
485 static int callback_before_layouts (Layout *a, Uint16 w, Uint16 h);
486 static int callback_after_layouts (Layout *a);
487 static void redraw_layout (Layout *a);
488 static void refresh_layout (Layout *a);
489 static int layout (Layout *root, Sint16 x, Sint16 y, Uint16 w, Uint16 h);
490 
491 /* TextOutput layouts. */
492 static void free_text_output(TextOutput *t);
493 static void init_all_text_outputs (void);
494 static int doclear_textoutput (Panel *p, void *parms);
495 static void doclose_textoutput (void *parms);
496 static int dorefresh_textoutput (Panel *p, void *parms);
497 static TextOutput *alloc_text_output (Layout *a);
498 static void clear_to_eol (TextOutput *p);
499 static void cr (TextOutput *t);
500 static void emit (TextOutput *t, unsigned char c, int colix);
501 static void backspace (TextOutput *t);
502 static void backchar (TextOutput *t);
503 static void delchar (TextOutput *t);
504 static void type (TextOutput *t, const char *str, int colix);
505 static void type_nowrap (TextOutput *t, const char *str, int colix);
506 
507 /* TextInput layouts. */
508 static void free_text_input (TextInput *t);
509 static void init_all_text_inputs (void);
510 static void cursoron (TextInput *t);
511 static void cursoroff (TextInput *t);
512 static int after_layout_textinput (Panel *p, void *parms);
513 static int doredraw_textinput (Panel *p, void *parms);
514 static void doclose_textinput (void *parms);
515 static int dorefresh_textinput (Panel *p, void *parms);
516 static TextInput * alloc_text_input (Layout *a);
517 static void activate (TextInput *t);
518 static void deactivate (TextInput *t);
519 static int is_active (TextInput *t);
520 static void give_focus (TextInput *t);
521 static void take_focus (TextInput *t);
522 static void set_prompt (TextInput *t, const char *prompt);
523 static void add_char (TextInput *t, char c);
524 static void del_char (TextInput *t);
525 static void reset_input (TextInput *t, const char *str);
526 static void finish_input (TextInput *t, char *buf, size_t maxbuf);
527 static void hide_input (TextInput *t);
528 
529 /* Core Arena layout. */
530 static Uint32 get_cell_size (const CellInfo *c);
531 static CellInfo choose_cell (int mode);
532 static void display_box (int addr, Uint32 boxes, int colix);
533 static int decr_cell_size (CellInfo *c);
534 static int before_layout_arena (Layout *layout, Uint16 w, Uint16 h);
535 static int after_layout_arena (Panel *p, void *parms);
536 static void doclose_arena (void *parms);
537 static int doredraw_arena (Panel *p, void *parms);
538 static int doclear_arena (Panel *p, void *parms);
539 static void make_blitboxes_for_colix (Arena *a, int colix);
540 static void make_blitbox (Arena *a, int colix, Uint32 boxes);
541 static void fill_box (SDL_Surface *s, Sint16 x, Sint16 y, Uint16 w, Uint16 h, Uint32 col);
542 static void free_blitboxes(Arena *a);
543 static void doremode_arena (void *parms);
544 static void init_arena (Layout *layout, size_t coresize, int displaymode);
545 
546 /* Status line, warrior names, process/cycles meters layouts. */
547 static int doredraw_status (Panel *p, void *parms);
548 static int doredraw_names (Panel *p, void *parms);
549 static int doredraw_meters (Panel *p, void *parms);
550 static int after_layout_meters (Panel *p, void *parms);
551 
552 /* Pulling it all together. */
553 static void redraw (void);
554 static void relayout (Uint16 w, Uint16 h);
555 static void init_layout (void);
556 
557 /* Cdb text panels. */
558 static int split_text_panel (int panel, int size, Uint16 method);
559 static void close_text_panel (int panel);
560 
561 /* Events and keyboard input. */
562 static void default_handler (SDL_Event *e);
563 static char conv_key (const SDL_keysym *s);
564 static int macro_key (const SDL_keysym *s, char *buf, int maxbuf);
565 
566 /* Command History */
567 static Cmd * get_cmd_ring();
568 static void add_cmd (const char *cmd);
569 
570 /* Events & keyboard. */
571 static void default_handler (SDL_Event *e);
572 static int special_keyhandler (const SDL_keysym *s);
573 static char conv_key (const SDL_keysym *s);
574 static int macro_key (const SDL_keysym *s, char *buf, int maxbuf);
575 
576 /* Misc. */
577 static void exit_hook (void);
578 
579 
580 /* ------------------------------------------------------------------------
581  * Utilities
582  */
583 
584 #undef max
585 #undef min
586 #define max(a,b)  ((a) < (b) ? (b) : (a))
587 #define min(a,b)  ((a) < (b) ? (a) : (b))
588 
589 /* Exit in a hurry. */
590 static void
exit_panic(const char * msg,const char * reason)591 exit_panic(const char *msg, const char *reason)
592 {
593     fprintf(stderr, errorHeader, msg);
594     if (reason) {
595 	fprintf(stderr, ": %s", reason);
596     }
597     fprintf(stderr, ".\n");
598     Exit(1);
599 }
600 
601 /* Safer strncpy() that always nil-terminates (except when maxbuf==0). */
602 static void
xstrncpy(char * dst,const char * src,size_t maxbuf)603 xstrncpy(char *dst, const char *src, size_t maxbuf)
604 {
605     size_t k;
606     if (maxbuf==0) { return; }
607     for (k=0; k<maxbuf-1 && src[k]; k++) {
608 	dst[k] = src[k];
609     }
610     dst[k] = 0;
611 }
612 
613 /* Saner strncat(). */
614 static void
xstrncat(char * dst,const char * src,size_t maxbuf)615 xstrncat(char *dst, const char *src, size_t maxbuf)
616 {
617     size_t len, k;
618     if (maxbuf==0) { return; }
619     len = strlen(dst);
620     for (k=0; k+len+1<maxbuf && src[k]; k++) {
621 	dst[k+len] = src[k];
622     }
623     dst[k+len] = 0;
624 }
625 
626 /* Append a character. */
627 static void
xstrnapp(char * dst,unsigned int c,size_t maxbuf)628 xstrnapp(char *dst, unsigned int c, size_t maxbuf)
629 {
630     size_t len;
631     if (maxbuf==0) { return; }
632     len = strlen(dst);
633     if (len+1 < maxbuf) {
634 	dst[len] = c;
635     }
636     dst[len+1] = 0;
637 }
638 
639 /* Copy a string. */
640 static char *
xstrdup(const char * s)641 xstrdup(const char *s)
642 {
643     char *t;
644     assert(s);
645     t = MALLOC((strlen(s)+1)*sizeof(char));
646     if (t==NULL) {
647 	exit_panic(outOfMemory, NULL);
648     }
649     { char *q = t;  while ((*q++ = *s++)) {} }
650     return t;
651 }
652 
653 /* Compute the clip rectangle R \cap C */
654 static void
clip_rect(const SDL_Rect * c,SDL_Rect * r)655 clip_rect(const SDL_Rect *c, SDL_Rect *r)
656 {
657     Sint16 x1,y1;
658     Sint16 x0,y0;
659     x0 = max(r->x, c->x);
660     y0 = max(r->y, c->y);
661     x1 = min(r->x+r->w, c->x+c->w);
662     y1 = min(r->y+r->h, c->y+c->h);
663     r->x = x0;
664     r->y = y0;
665     r->w = max(x1-x0,0);
666     r->h = max(y1-y0,0);
667 }
668 
669 /* C := smallest rectangle containing both R and C. */
670 static void
join_rect(const SDL_Rect * r,SDL_Rect * c)671 join_rect(const SDL_Rect *r, SDL_Rect *c)
672 {
673     if (c->w && c->h) {
674 	Sint16 x0,y0;
675 	Sint16 x1,y1;
676 	x0 = min(r->x, c->x);
677 	y0 = min(r->y, c->y);
678 	x1 = max(r->x+r->w, c->x+c->w);
679 	y1 = max(r->y+r->h, c->y+c->h);
680 	c->x = x0;
681 	c->y = y0;
682 	c->w = x1-x0;
683 	c->h = y1-y0;
684     } else {
685 	*c = *r;
686     }
687 }
688 
689 
690 /*
691  * Set the pixel at (x, y) to the given value
692  * NOTE: The surface must be locked before calling this!
693  * This is from the SDL docs, so it can't be all that bad.
694  */
695 static void
putpixel(SDL_Surface * surface,int x,int y,Uint32 pixel)696 putpixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
697 {
698     int bpp = surface->format->BytesPerPixel;
699     /* Here p is the address to the pixel we want to set */
700     Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
701 
702     switch(bpp) {
703     case 1:
704         *p = pixel;
705         break;
706 
707     case 2:
708         *(Uint16 *)p = pixel;
709         break;
710 
711     case 3:
712         if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
713             p[0] = (pixel >> 16) & 0xff;
714             p[1] = (pixel >> 8) & 0xff;
715             p[2] = pixel & 0xff;
716         } else {
717             p[0] = pixel & 0xff;
718             p[1] = (pixel >> 8) & 0xff;
719             p[2] = (pixel >> 16) & 0xff;
720         }
721         break;
722 
723     case 4:
724         *(Uint32 *)p = pixel;
725         break;
726     }
727 }
728 
729 /* ------------------------------------------------------------------------
730  * Display surface & colour management
731  *
732  * Globals:
733  *  SDL_Surface *TheSurf; (read/write)
734  *     Display surface.
735  *
736  *  VMode TheVMode;
737  *     Global video mode.
738  *
739  *  NCOLOURS (= 16) (read-only)
740  *	The number of colours in _our_ palette.  pMARS uses only the 16
741  *	standard VGA colours for a uniform look-and-feel. :)
742  *
743  *  static const RGBColour PaletteRGB[NCOLOURS]; (read-only)
744  *	RGBValues of _our_ palette.  Indexed by <colourname> #defines:
745  *
746  *  Uint32 Colours[NCOLOURS]; (read-only)
747  *	The colours in PaletteRGB[] in the format of the display surface.
748  *	These must be initialised using SDL_MapRGB() before use.obs
749  *
750  * Functions:
751  *  const char *decode_vmode_string(VMode *mode, const char* argstr);
752  *	Decodes a videomode string ARGSTR and places the results in
753  *	the video mode info structure MODE.  Returns NULL on success.
754  *	On failure, returns a pointer to the trailing substring that
755  *	wasn't understood.
756  *
757  *  SDL_Surface *set_VMode(VMode* mode)
758  *	Attempt to set the display video mode to the given MODE and return
759  *	a pointer to the opened surface --- NULL on failure.  On success
760  *	the flags of MODE are set to indicate the actual flags of the surface,
761  *	and the colours from RGBColours are mapped into display-native
762  *	colours into Colours[].
763  *
764  *  void Slock(SDL_Surface *);
765  *  void Sunlock(SDL_Surface *);
766  *	Lock/Unlock the given surface if necessary.
767  */
768 
769 
770 /* Decode VMode options from a VMode string.  On success, the given
771  * VMode structure contains the desired mode, and the function returns
772  * NULL.  Otherwise the function returns a non-NULL pointer to a
773  * trailing substring of the mode string that doesn't make sense.
774  * The option string must have this format:
775  *	[<width>x<height>][@<bpp>][:<flag1>[:<flag2>[...[:<flagN>]...]]]
776  * The flags may be preceded by a '-' to indicate that the given
777  * flag must be cleared.  Valid examples:
778  *
779  *	640x480				640 by 480 at any bpp, window.
780  *	640x480x16			640 by 480 at 8 bits per pixel window.
781  *	320x200x16:noframe		320 by 200 window without a frame.
782  *	:fullscreen			default width/height, fullscreen.
783  */
784 static const char*
decode_vmode_string(VMode * m,const char * str)785 decode_vmode_string(VMode *m, const char *str)
786 {
787     int tmp;
788     int set_dims = 0;
789     /* Decode width, height, bpp in the format <WIDTH>x<HEIGHT>[@<BPP>].
790      * bpp==0 means that any pixel format is acceptable (most probably the
791      * current pixel format of the display device).
792      */
793     m->w = DEFAULT_VMODE_W;
794     m->h = DEFAULT_VMODE_H;
795     m->bpp = DEFAULT_VMODE_BPP;
796     m->flags = DEFAULT_VMODE_FLAGS;
797     if (1==sscanf(str, "%d", &tmp)) {
798 	if (3!=sscanf(str,"%hux%hu@%hu", &m->w, &m->h, &m->bpp)) {
799 	    m->w = DEFAULT_VMODE_W;
800 	    m->h = DEFAULT_VMODE_H;
801 	    m->bpp = DEFAULT_VMODE_BPP;
802 	    if (2!=sscanf(str, "%hux%hu", &m->w, &m->h)) {
803 		return str;
804 	    }
805 	}
806 	set_dims = 1;
807 	while (*str && *str!=':') { str++; } /* skip to ':' */
808     } else if (*str=='@') {
809 	if (1!=sscanf(str,"@%hu", &m->bpp)) {
810 	    m->bpp = DEFAULT_VMODE_BPP;
811 	    return str;
812 	}
813 	while (*str && *str!=':') { str++; } /* skip to ':' */
814     }
815     if (*str==':') { str++; }		/* skip over ':' */
816 
817     /* Decode flags separated by ':' and optionally preceded by '-'. */
818     if (m->bpp == 0) { m->flags |= SDL_ANYFORMAT; }
819 
820     while (*str) {
821 	int i;
822 	int set_them = 1;
823 	if (str[0]=='-') { set_them = 0; str++; }
824 
825 	for (i=0; i<NUM_VMODE_FLAGS; i++) {
826 	    if (strlen(str)>=VMode_flags[i].siglen) {
827 		if (0==strncmp(str, VMode_flags[i].name,
828 			       VMode_flags[i].siglen))
829 		{
830 		    if (set_them) {
831 			m->flags |= VMode_flags[i].value;
832 		    } else {
833 			m->flags &= ~VMode_flags[i].value;
834 		    }
835 		    break;
836 		}
837 	    }
838 	}
839 	if (i==NUM_VMODE_FLAGS) {
840 	    return str;		/* unknown flag */
841 	}
842 	while (*str && *str!=':') { str++; } /* skip to ':' */
843 	if (*str) { str++; }	/* skip over ':' */
844     }
845 
846     /* If the user didn't set the dimensions and requested fullscreen mode,
847      * we should find the biggest fullscreen mode available and use that.
848      * In case no modes are available, or "all" modes are ok, use the defaults.
849      */
850     if (!set_dims && ((m->flags & SDL_FULLSCREEN)==SDL_FULLSCREEN)) {
851 	SDL_Rect **modes;
852 	modes = SDL_ListModes(NULL, SDL_FULLSCREEN);
853 	if (modes == NULL || modes==(SDL_Rect**)(-1)) {
854 	    /* Use defaults that are set already. */
855 	} else {
856 	    /* Find the biggest fullscreen mode. */
857 	    int i, bigix = -1;
858 	    double bigsize = 0;
859 	    for (i=0; modes[i]; i++) {
860 		double size = (double)modes[i]->w*modes[i]->h;
861 		if (size>bigsize) {
862 		    bigsize = size;
863 		    bigix = i;
864 		}
865 	    }
866 	    if (bigix>=0) {
867 		m->w = modes[bigix]->w;
868 		m->h = modes[bigix]->h;
869 	    }
870 	}
871     }
872 
873     return 0;				/* all ok. */
874 }
875 
876 /* Lock a surface if necessary. */
877 static void
Slock(SDL_Surface * screen)878 Slock(SDL_Surface *screen)
879 {
880     if ( SDL_MUSTLOCK(screen) )
881     {
882 	while (SDL_LockSurface(screen)<0) {
883 	    SDL_Delay(50);		/* wait for a while. */
884 	}
885     }
886 }
887 
888 /* Unlock a surface if necessary. */
889 static void
Sunlock(SDL_Surface * screen)890 Sunlock(SDL_Surface *screen)
891 {
892     if ( SDL_MUSTLOCK(screen) )
893     {
894 	SDL_UnlockSurface(screen);
895     }
896 }
897 
898 /* Attempt to initialise the display to a given video mode M.  Returns
899  * the surface structure of the display if successful and modifies the
900  * VMode structure M to reflect the actual flags of the surface.  On
901  * failure returns NULL.  On success, remaps the colours in Colours[] to
902  * the display native format as a side-effect.
903  **/
904 static SDL_Surface *
set_VMode(VMode * m)905 set_VMode(VMode* m)
906 {
907     SDL_Surface *surf = NULL;
908     Uint32 flags = m->flags;
909 
910     if ((flags & SDL_FULLSCREEN) == SDL_FULLSCREEN) { /* only in fullscreen. */
911 	const SDL_VideoInfo *info = SDL_GetVideoInfo();
912 	if ( info->blit_fill ) {
913 	    /* We want accelerated blitting. */
914 	    flags |= SDL_HWSURFACE;
915 	}
916 	if ((flags & SDL_HWSURFACE) == SDL_HWSURFACE) {
917 	    /* Direct hardware blitting without double-buffering
918 	     * causes really bad flickering.
919 	     */
920 	    if ( m->bpp>0
921 		 && info->video_mem*1024 > (Uint32)(m->h*m->w*m->bpp/8) )
922 	    {
923 		flags |= SDL_DOUBLEBUF;
924 	    } else {
925 		flags &= ~SDL_HWSURFACE;
926 	    }
927 	}
928 	if ((flags & SDL_HWSURFACE) == SDL_HWSURFACE) {
929 	    flags &=~ SDL_ANYFORMAT;
930 	}
931     }
932 
933     if (!(surf = SDL_SetVideoMode(m->w, m->h, m->bpp, flags))
934 	&& flags != m->flags)
935     {
936 	flags = m->flags;		/* try with original flags. */
937 	surf = SDL_SetVideoMode(m->w, m->h, m->bpp, flags);
938     }
939     m->flags = flags;
940 
941     /* Update VMode with the one we actually got. */
942     TheSurf = surf;
943     if (surf) {
944 	m->flags = surf->flags;
945 	m->w = surf->w;
946 	m->h = surf->h;
947 	m->bpp = surf->format->BitsPerPixel;
948 
949 	/* Set the caption on our window.
950 	 */
951 	SDL_WM_SetCaption("pMARS", "pMARS");
952 #if 0
953 	printf("%dx%d@%d:%x\n", m->w, m->h, m->bpp, m->flags);
954 #endif
955 
956 	/* Map the colours we will use into display-native format. */
957 	{
958 	    int k;
959 	    for (k=0; k<NCOLOURS; k++) {
960 		RGBColour c = PaletteRGB[k];
961 		Colours[k] = SDL_MapRGB(surf->format, c.r, c.g, c.b);
962 	    }
963 	}
964 
965 	/* Initialise the font for the colours we will use. */
966 	Font_Init(&CurrentFont, fnt16_raw, 8, 16, 128);
967     }
968     return surf;
969 }
970 
971 
972 #if 0
973 static void
974 clear_display(void)
975 {
976     SDL_Rect r;
977     r.x = 0;
978     r.y = 0;
979     r.w = TheSurf->w;
980     r.h = TheSurf->h;
981     Slock(TheSurf);
982     SDL_FillRect(TheSurf, &r, Colours[BLACK]);
983     Sunlock(TheSurf);
984 }
985 #endif
986 
987 
988 static void
refresh_rect(const SDL_Rect * r)989 refresh_rect(const SDL_Rect *r)
990 {
991     SDL_UpdateRect(TheSurf, r->x, r->y, r->w, r->h);
992 }
993 
994 /* ------------------------------------------------------------------------
995  * Text and font routines.
996  *
997  * Globals:
998  *   struct FixedFontInfo_st CurrentFont; (read-only)
999  *
1000  * Functions:
1001  *  void outcharxy(unsigned char c, Sint16 x, Sint16 y, Uint32 col,
1002  *		   const SDL_Rect *clip);
1003  *	Draw the glyph for character C in the current font in the
1004  *	given (display format) colour COL into the global display
1005  *	surface TheSurf.  Clip to the rectangle CLIP if it is non-NULL.
1006  *
1007  *   void outblankxy(Sint16 x, Sint16 y, Uint32 col, const SDL_Rect *clip);
1008  *	Fill an 8 by HEIGHT (of the current font) rectangle whose top-left
1009  *	coordinate is (X,Y) with the given colour.  Clip to CLIP is non-NULL.
1010  *
1011  *   void Font_Init(FixedFontInfo *f, const Uint8 *bitmaps,
1012  *		Uint16 w, Uint16 h, Uint16 nchars);
1013  *	Initialise the font into display native format.
1014  **/
1015 
1016 static void
Font_Init(FixedFontInfo * f,const Uint8 * bitmaps,Uint16 w,Uint16 h,Uint16 nchars)1017 Font_Init(FixedFontInfo *f, const Uint8* bitmaps, Uint16 w, Uint16 h, Uint16 nchars)
1018 {
1019     int i;
1020     Uint32 key;
1021     f->bitmaps = bitmaps;
1022     f->w = w;
1023     f->h = h;
1024     f->nchars = nchars;
1025 
1026     /* If bitmaps == NULL, then this is the first call to initialise a
1027      * font and we should load all the display native format surfaces
1028      * with NULL so deallocation works when it is properly initialised. */
1029     if (bitmaps==NULL) {
1030 	for (i=0; i<NCOLOURS; i++) {
1031 	    f->glyphs[i] = NULL;	/* clear glyphs for all colours. */
1032 	}
1033 	return;
1034     }
1035 #if USE_BLIT_FONTS
1036     /* Free the earlier display native format glyphs. */
1037     for (i=0; i<NCOLOURS; i++) {
1038 	if (f->glyphs[i]) {
1039 	    SDL_FreeSurface(f->glyphs[i]);
1040 	    f->glyphs[i] = NULL;
1041 	}
1042     }
1043 
1044     /* Create the new glyphs for each colour. */
1045     for (i=0; i<NCOLOURS; i++) {
1046 	int c;
1047 	Uint32 col;
1048 	SDL_Surface *s;
1049 	s = SDL_CreateRGBSurface(SDL_ANYFORMAT, w*nchars, h,
1050 				 TheSurf->format->BitsPerPixel,
1051 				 TheSurf->format->Rmask,
1052 				 TheSurf->format->Gmask,
1053 				 TheSurf->format->Bmask,
1054 				 0);	/* no alpha. */
1055 	if (s == NULL) {
1056 	    exit_panic(errSpriteSurf, SDL_GetError());
1057 	}
1058 
1059 	/* Set colorkey (=black) for this glyphmap. */
1060 	key = SDL_MapRGB(s->format, 0, 0, 0); /* colorkey */
1061 	if (-1==SDL_SetColorKey(s, SDL_RLEACCEL, key)) {
1062 	    exit_panic(errSpriteColKey, SDL_GetError());
1063 	}
1064 
1065 	/* Get the foreground colour and clear the background to the
1066 	 * colorkey. */
1067 	col = SDL_MapRGB(s->format, PaletteRGB[i].r,
1068 			 PaletteRGB[i].g, PaletteRGB[i].b);
1069 	fill_box(s, 0, 0, c*w*h, h, key);
1070 
1071 	/* Draw the individual characters (vewy slowly :/) */
1072 	Slock(s);
1073 	for (c=0; c<nchars; c++) {
1074 	    Sint16 x, y;
1075 	    for (y=0; y<h; y++) {
1076 		for (x=0; x<w; x++) {
1077 		    /* Get a bit from the bitmap.  The MSB in a byte
1078 		     * is the leftmost. */
1079 		    Uint32 bitno = c*w*h + y*w + x;
1080 		    Uint32 ix = bitno/8;
1081 		    Uint8 row = f->bitmaps[ix];
1082 		    bitno = 7 - (bitno & 7);
1083 		    if (row & (1<<bitno)) {
1084 			putpixel(s, c*w+x, y, col);
1085 		    }
1086 		}
1087 	    }
1088 	}
1089 	Sunlock(s);
1090 
1091 	/* Convert glyphs to video format. */
1092 	f->glyphs[i] = SDL_DisplayFormat(s);
1093 	SDL_FreeSurface(s);
1094 	if (f->glyphs[i] == NULL) {
1095 	    exit_panic(errSpriteConv, SDL_GetError());
1096 	}
1097     }
1098 #endif
1099 }
1100 
1101 /* Clear a character sized area. */
1102 static void
outblankxy(Sint16 x,Sint16 y,int colix,const SDL_Rect * clip)1103 outblankxy(Sint16 x, Sint16 y, int colix, const SDL_Rect *clip)
1104 {
1105     SDL_Rect r;
1106     r.x = x; r.y = y;
1107     r.w = CurrentFont.w;
1108     r.h = CurrentFont.h;
1109     if (!clip) {
1110 	SDL_Rect c;
1111 	c.x = c.y = 0;
1112 	c.w = TheSurf->w; c.h = TheSurf->h;
1113 	clip_rect(&c, &r);
1114     } else {
1115 	clip_rect(clip, &r);
1116     }
1117     Slock(TheSurf);
1118     SDL_FillRect(TheSurf, &r, Colours[colix]);
1119     Sunlock(TheSurf);
1120 }
1121 
1122 /* Draw a character at a position. */
1123 static void
outcharxy(unsigned char c,Sint16 x,Sint16 y,int colix,const SDL_Rect * clip)1124 outcharxy(unsigned char c, Sint16 x, Sint16 y, int colix,
1125 	  const SDL_Rect *clip)
1126 {
1127     SDL_Rect oldclip;
1128     SDL_Rect src, dst;
1129 #if USE_BLIT_FONTS
1130     if (c >= CurrentFont.nchars) { return; }
1131     src.h = dst.h = CurrentFont.h;
1132     src.w = dst.w = CurrentFont.w;
1133     dst.x = x; dst.y = y;
1134     src.x = c*CurrentFont.w;
1135     src.y = 0;
1136     SDL_GetClipRect(TheSurf, &oldclip);
1137     SDL_SetClipRect(TheSurf, clip);
1138 
1139     SDL_BlitSurface(CurrentFont.glyphs[colix], &src, TheSurf, &dst);
1140 
1141     SDL_SetClipRect(TheSurf, &oldclip);
1142 #else
1143     SDL_Rect r;
1144     int i,j,h;
1145     if (c >= CurrentFont.nchars) { return; }
1146 
1147     if (!clip) {
1148 	r.x = r.y = 0;
1149 	r.w = TheSurf->w;
1150 	r.h = TheSurf->h;
1151     } else {
1152 	r = *clip;
1153     }
1154     outblankxy(x,y, BLACK, &r);
1155     if (c==' ') { return; }
1156 
1157     h = CurrentFont.h;
1158     assert(CurrentFont.w == 8);
1159 
1160     /* Draw character pixel by pixel.  Replace with something faster
1161      * if it's too slow. */
1162     Slock(TheSurf);
1163     for (j=r.y; j<r.y+r.h; j++) {
1164 	Uint8 row = CurrentFont.bitmaps[c*h+j-y];
1165 	for (i=r.x; i<r.x+r.w; i++) {
1166 	    if (row & (1<<(7-(i-x)))) {
1167 		putpixel(TheSurf, i, j, Colours[colix]);
1168 	    }
1169 	}
1170     }
1171     Sunlock(TheSurf);
1172 #endif /* USE_BLIT_FONTS */
1173 }
1174 
1175 
1176 /* ------------------------------------------------------------------------
1177  * Panels
1178  *
1179  * Panels are rectangular areas on the display surface that may have a border.
1180  **/
1181 
1182 /* Create a new border. */
1183 static Border
border(Sint16 l,Sint16 r,Sint16 t,Sint16 b,int colix)1184 border(Sint16 l, Sint16 r, Sint16 t, Sint16 b, int colix) {
1185     Border e;
1186     e.lft = l; e.rgt = r; e.top = t; e.bot = b; e.colix = colix;
1187     return e;
1188 }
1189 
1190 /* Draw a border. */
1191 static void
draw_border(const SDL_Rect * r,const Border * b)1192 draw_border(const SDL_Rect *r, const Border *b)
1193 {
1194     SDL_Rect q;
1195     if (b->lft > 0 && r->h>0) {
1196 	q = *r; q.w = 1;
1197 	SDL_FillRect(TheSurf, &q, Colours[b->colix]);
1198     }
1199     if (b->rgt > 0 && r->w>0 && r->h>0) {
1200 	q = *r; q.w = 1; q.x += r->w-1;
1201 	SDL_FillRect(TheSurf, &q, Colours[b->colix]);
1202     }
1203     if (b->top > 0 && r->w>0) {
1204 	q = *r; q.h = 1;
1205 	SDL_FillRect(TheSurf, &q, Colours[b->colix]);
1206     }
1207     if (b->bot > 0 && r->h>0 && r->w>0) {
1208 	q = *r; q.h = 1; q.y += r->h-1;
1209 	SDL_FillRect(TheSurf, &q, Colours[b->colix]);
1210     }
1211 }
1212 
1213 /* Set the view rectangle of a panel. */
1214 static void
recompute_panel_view(Panel * p)1215 recompute_panel_view(Panel *p)
1216 {
1217     p->v = p->r;
1218     p->v.x += abs(p->b.lft);
1219     p->v.y += abs(p->b.bot);
1220     p->v.w = 0;
1221     if (p->r.w > abs(p->b.lft) + abs(p->b.rgt)) {
1222 	p->v.w = p->r.w - (abs(p->b.lft) + abs(p->b.rgt));
1223     }
1224     p->v.h = 0;
1225     if (p->r.h > abs(p->b.top) + abs(p->b.bot)) {
1226 	p->v.h = p->r.h - (abs(p->b.top) + abs(p->b.bot));
1227     }
1228 }
1229 
1230 /* Create a new panel. */
1231 static Panel
new_panel(Sint16 x,Sint16 y,Uint16 w,Uint16 h,Border b)1232 new_panel(Sint16 x, Sint16 y, Uint16 w, Uint16 h, Border b)
1233 {
1234     Panel p;
1235     p.r.x = x;
1236     p.r.y = y;
1237     p.r.w = w;
1238     p.r.h = h;
1239     p.b = b;
1240     recompute_panel_view(&p);
1241     return p;
1242 }
1243 
1244 /* Return the "view rectangle" that contains the contents of the panel. */
1245 static const SDL_Rect*
panel_view(const Panel * p)1246 panel_view(const Panel *p)
1247 {
1248     return &p->v;
1249 }
1250 
1251 /* Make sure the panel area is updated on the screen. */
1252 static void
refresh_panel(const Panel * p)1253 refresh_panel(const Panel *p)
1254 {
1255     refresh_rect(&p->r);
1256 }
1257 
1258 /* Return the size of the panel's view rectangle in columns and rows
1259  * of the current font.  */
1260 static void
panel_text_size(const Panel * p,int * cols,int * lines)1261 panel_text_size(const Panel *p, int *cols, int *lines)
1262 {
1263     const SDL_Rect *r = panel_view(p);
1264     if (cols) { *cols = r->w / CurrentFont.w; }
1265     if (lines) { *lines = r->h / CurrentFont.h; }
1266 }
1267 
1268 /* Clear a rectangle. */
1269 static void
clear_rect(const SDL_Rect * r)1270 clear_rect(const SDL_Rect *r)
1271 {
1272     SDL_Rect c = *r;
1273     Slock(TheSurf);
1274     SDL_FillRect(TheSurf, &c, Colours[BLACK]);
1275     Sunlock(TheSurf);
1276 }
1277 
1278 
1279 /* Clear the contents of the panel and draw the border. */
1280 static void
clear_panel(Panel * p)1281 clear_panel(Panel *p)
1282 {
1283     SDL_Rect r = p->r;
1284     SDL_FillRect(TheSurf, &r, Colours[BLACK]);
1285     draw_border(&r, &p->b);
1286 }
1287 
1288 
1289 /* ------------------------------------------------------------------------
1290  * Generic panel layout (with ideas cheerfully stolen from Andrew Plotkin's
1291  * GLK model.)
1292  *
1293  * Panel are isolated entities and don't know how to react to resizing and
1294  * other layout affecting events.  "Layout" structures link panels together
1295  * so that they may be layed out in simple ways.  New layouts are created
1296  * by splitting previously defined layouts, in which case the new layout
1297  * takes some part of the space of the splitted layout.
1298  *
1299  * When a layout is split, it is "replaced" by an internally generated
1300  * layout that has the old layout and the new layout as it's children.  By
1301  * splitting layouts like this, they form a binary tree with the leaf nodes
1302  * corresponding to actual panels that are drawn onto, and the internal
1303  * nodes to internally generated layout structures (called pair layouts.)
1304  **/
1305 
1306 /* Initialise the statically allocated pool of layout structures. */
1307 static void
init_all_layouts(void)1308 init_all_layouts(void)
1309 {
1310     static int done_init = 0;
1311     if (!done_init) {
1312 	int i;
1313 	memset((void*)all_layouts, 0, MAXLAYOUTS*sizeof(Layout));
1314 	layout_free_list = &all_layouts[0];
1315 	for (i=0; i<MAXLAYOUTS-1; i++) {
1316 	    all_layouts[i].a = &all_layouts[i+1];
1317 	}
1318 	all_layouts[MAXLAYOUTS-1].a = NULL;
1319 	done_init = 1;
1320     }
1321 }
1322 
1323 /* Default action to take before laying out a layout into a display
1324  * of size w by h pixels. */
1325 static int
default_before_layoutfunc(Layout * a,Uint16 w,Uint16 h)1326 default_before_layoutfunc(Layout *a, Uint16 w, Uint16 h)
1327 {
1328     a = a; w = w; h = h;
1329     return 1;
1330 }
1331 
1332 /* Default action to take after laying out. */
1333 static int
default_after_layoutfunc(Panel * p,void * parms)1334 default_after_layoutfunc(Panel *p, void *parms)
1335 {
1336     parms = parms; p = p;
1337     return 1;
1338 }
1339 
1340 /* Default redraw only draws the borders of a panel. */
1341 static int
default_redrawfunc(Panel * p,void * parms)1342 default_redrawfunc(Panel *p, void *parms)
1343 {
1344     parms = parms;
1345     draw_border(&p->r, &p->b);
1346     return 1;
1347 }
1348 
1349 /* Default clear only clears a panel (and possibly draws borders.) */
1350 static int
default_clearfunc(Panel * p,void * parms)1351 default_clearfunc(Panel *p, void *parms)
1352 {
1353     parms = parms;
1354     clear_panel(p);
1355     return 1;
1356 }
1357 
1358 /* Default refresh refreshes the panel. */
1359 static int
default_refreshfunc(Panel * p,void * parms)1360 default_refreshfunc(Panel *p, void *parms)
1361 {
1362     parms = parms;
1363     refresh_panel(p);
1364     return 1;
1365 }
1366 
1367 /* Default action to take on closing a panel. */
1368 static void
default_closefunc(void * parms)1369 default_closefunc(void *parms)
1370 {
1371     parms = parms;
1372 }
1373 
1374 /* Default action to take when the display mode has changed. */
1375 static void
default_remodefunc(void * parms)1376 default_remodefunc(void *parms)
1377 {
1378     parms = parms;
1379 }
1380 
1381 /* Allocate a new layout from the pool of statically allocated layouts. */
1382 static Layout *
alloc_layout(void)1383 alloc_layout(void)
1384 {
1385     Layout *lay;
1386     init_all_layouts();
1387     lay = layout_free_list;
1388     assert(lay && "Ran out of layouts.");
1389     layout_free_list = lay->a;
1390 
1391     lay->p = new_panel(0,0, 0,0, noborder);
1392     lay->a = lay->b = NULL;
1393     lay->minw = lay->minh = 0;
1394     lay->maxw = lay->maxh = UNBOUNDED;
1395     lay->before_layout = default_before_layoutfunc;
1396     lay->after_layout = default_after_layoutfunc;
1397     lay->doclear = default_clearfunc;
1398     lay->doredraw = default_redrawfunc;
1399     lay->doclose = default_closefunc;
1400     lay->doremode = default_remodefunc;
1401     lay->dorefresh = default_refreshfunc;
1402     lay->parms = NULL;
1403     lay->parent = NULL;
1404     lay->method = 0;
1405     lay->size = 0;
1406     return lay;
1407 }
1408 
1409 /* Allocate a pair-layout (internal layout). */
1410 static Layout *
alloc_pair_layout(void)1411 alloc_pair_layout(void)
1412 {
1413     Layout *pair = alloc_layout();
1414     pair->before_layout = NULL;
1415     pair->after_layout = NULL;
1416     pair->doclear = NULL;
1417     pair->doredraw = NULL;
1418     pair->doclose = NULL;
1419     pair->doremode = NULL;
1420     pair->dorefresh = NULL;
1421     return pair;
1422 }
1423 
1424 /* Free a layout. */
1425 static void
free_layout(Layout * lay)1426 free_layout(Layout *lay)
1427 {
1428     if (lay->doclose) { lay->doclose(lay->parms); }
1429     lay->a = layout_free_list;
1430     layout_free_list = lay;
1431 }
1432 
1433 /* Saturating addition of width|height bounds. */
1434 static Uint16
add_bounds(Uint32 a,Uint32 b)1435 add_bounds(Uint32 a, Uint32 b)
1436 {
1437     Uint32 c = a + b;
1438     return (Uint16)(c <= UNBOUNDED ? c : UNBOUNDED);
1439 }
1440 
1441 #if 0
1442 /* Saturating(?) subtraction of width|height bounds. */
1443 static Uint16
1444 sub_bounds(Uint32 a, Uint32 b)
1445 {
1446     Sint32 c = a - b;
1447     if (a >= UNBOUNDED) { return UNBOUNDED; }
1448     return max(c, 0);
1449 }
1450 #endif
1451 
1452 /* Compute the minimum and maximum sizes for all pair layouts bottom-up
1453  * from the bounds of the leaf layouts. */
1454 static void
synthesize_bounds(Layout * pair)1455 synthesize_bounds(Layout *pair)
1456 {
1457     if (!pair->a && !pair->b) {
1458 	assert(pair->minw <= pair->maxw);
1459 	assert(pair->minh <= pair->maxh);
1460 	return;				/* not a pair. */
1461     }
1462     assert(pair->a && pair->b);
1463     synthesize_bounds(pair->a);
1464     synthesize_bounds(pair->b);
1465     if (is_horizontal_split(pair->method)) {
1466 	pair->minw = add_bounds(pair->a->minw, pair->b->minw);
1467 	pair->maxw = add_bounds(pair->a->maxw, pair->b->maxw);
1468 	pair->minh = max(pair->a->minh, pair->b->minh);
1469 	pair->maxh = max(pair->a->maxh, pair->b->maxh);
1470     } else {
1471 	pair->minh = add_bounds(pair->a->minh, pair->b->minh);
1472 	pair->maxh = add_bounds(pair->a->maxh, pair->b->maxh);
1473 	pair->minw = max(pair->a->minw, pair->b->minw);
1474 	pair->maxw = max(pair->a->maxw, pair->b->maxw);
1475     }
1476     assert(pair->minw <= pair->maxw);
1477     assert(pair->minh <= pair->maxh);
1478 }
1479 
1480 /* Return a freshly allocated (default) layout with the given
1481  * minimum/maximum bounds and border. */
1482 static Layout *
new_layout(Uint16 minw,Uint16 maxw,Uint16 minh,Uint16 maxh,Border b)1483 new_layout(Uint16 minw, Uint16 maxw,
1484 	   Uint16 minh, Uint16 maxh,
1485 	   Border b)
1486 {
1487     Layout *a = alloc_layout();
1488     assert(minw <= maxw);
1489     assert(minh <= maxh);
1490     a->p.b = b;
1491     a->minw = add_bounds(minw, abs(b.lft) + abs(b.rgt));
1492     a->minh = add_bounds(minh, abs(b.top) + abs(b.bot));
1493     a->maxw = add_bounds(maxw, abs(b.lft) + abs(b.rgt));
1494     a->maxh = add_bounds(maxh, abs(b.top) + abs(b.bot));
1495     return a;
1496 }
1497 
1498 /* Return the root layout structure of a layout. */
1499 static Layout *
get_root_layout(Layout * a)1500 get_root_layout(Layout *a)
1501 {
1502     while (a->parent) { a = a->parent; }
1503     return a;
1504 }
1505 
1506 /* Free all contained layouts bottom-up of a layout, including itself. */
1507 static void
recursive_free_layout(Layout * a)1508 recursive_free_layout(Layout *a)
1509 {
1510     if (a->a) { recursive_free_layout(a->a); }
1511     if (a->b) { recursive_free_layout(a->b); }
1512     free_layout(a);
1513 }
1514 
1515 /* Returns the sibling layout of a non-root layout. */
1516 static Layout *
get_sibling_layout(const Layout * a)1517 get_sibling_layout(const Layout *a)
1518 {
1519     if (a->parent == NULL) { return NULL; }
1520     if (a->parent->a != a) { return a->parent->a; }
1521     return a->parent->b;
1522 }
1523 
1524 /* Close a layout (and all child layouts).  There are three cases
1525  * depending on whether or not the layout's parent needs to be closed
1526  * too (layout A is the one we are closing):
1527  *
1528  * i)               ii)    O           iii)  G        G
1529  * A -> (nothing)         / \  -> B         / \  ->  / \
1530  *                       A   B             ?   O    ?   B
1531  *                                            / \
1532  *                                           A   B
1533  */
1534 static void
close_layout(Layout * a)1535 close_layout(Layout *a)
1536 {
1537     if (a->parent != NULL) {		/* Need to free a parent? */
1538 	Layout *parent = a->parent;
1539 	Layout *grandparent = parent->parent;
1540 	Layout *b = get_sibling_layout(a);
1541 	b->parent = grandparent;
1542 	if (grandparent != NULL) {	/* Need to replace parent with b? */
1543 	    if (grandparent->a == parent) {
1544 		grandparent->a = b;
1545 	    } else {
1546 		assert(grandparent->b == parent);
1547 		grandparent->b = b;
1548 	    }
1549 	}
1550 	free_layout(parent);
1551     }
1552     recursive_free_layout(a);
1553 }
1554 
1555 /* Return a new layout that splits the given leaf-layout. */
1556 static Layout *
split_layout(Layout * a,Uint32 method,Sint16 size,Uint16 mind,Uint16 maxd,Border border)1557 split_layout(Layout *a,			/* layout to split */
1558 	     Uint32 method,		/* split method */
1559 	     Sint16 size,		/* size of split */
1560 	     Uint16 mind, Uint16 maxd,
1561 	     Border border)		/* border */
1562 {
1563     Layout *b = alloc_layout();
1564     Layout *pair = alloc_pair_layout();
1565     Layout *parent;
1566     b->p.b = border;
1567     assert(!a->a && !a->b && "Can't split a pair layout.");
1568     assert(mind <= maxd);
1569     assert(a->minw <= a->maxw);
1570     assert(a->minh <= a->maxh);
1571 
1572     /* Fix parent relations. */
1573     parent = a->parent;
1574     pair->a = a;  a->parent = pair;
1575     pair->b = b;  b->parent = pair;
1576     pair->parent = parent;
1577     if (parent) {
1578 	if (parent->a == a) {
1579 	    parent->a = pair;
1580 	} else {
1581 	    assert(parent->b == a);
1582 	    parent->b = pair;
1583 	}
1584     }
1585 
1586     /* Set split info. */
1587     pair->size = size;
1588     pair->method = method;
1589 
1590     /* Set layout bounds. */
1591     if (is_horizontal_split(method)) {
1592 	Uint16 d = abs(border.lft) + abs(border.rgt);
1593 	b->minh = a->minh;
1594 	b->maxh = a->maxh;
1595 	b->minw = add_bounds(mind, d);
1596 	b->maxw = add_bounds(maxd, d);
1597     } else /* horizontal layout */ {
1598 	Uint16 d = abs(border.top) + abs(border.bot);
1599 	b->minw = a->minw;
1600 	b->maxw = a->maxw;
1601 	b->minh = add_bounds(mind, d);
1602 	b->maxh = add_bounds(maxd, d);
1603     }
1604     return b;
1605 }
1606 
1607 /* Auxiliary function to compute sizes *WA and *WB that use together
1608  * up to W pixels (in width or height), alloting PERCENTAGE of W to
1609  * *WB, but taking note of minimum and maximum bounds for the sizes of
1610  * A and B. */
1611 static void
choose_sizes(Uint32 w,Uint32 percentage,Uint32 amin,Uint32 amax,Uint32 bmin,Uint32 bmax,Uint16 * wa,Uint16 * wb)1612 choose_sizes(
1613     Uint32 w,				/* available size. */
1614     Uint32 percentage,			/* percentage alloted to B. */
1615     Uint32 amin, Uint32 amax,		/* bounds for A. */
1616     Uint32 bmin, Uint32 bmax,		/* bounds for B. */
1617     Uint16 *wa,  Uint16 *wb)		/* result pointers. */
1618 {
1619     if (w > amax + bmax) {
1620 	*wa = amax;
1621 	*wb = bmax;
1622     } else {
1623 	Uint32 w1, w2;
1624 	w2 = w * percentage / 100;
1625 	w2 = min(w2, bmax);
1626 	w2 = max(w2, bmin);
1627 
1628 	w1 = w - w2;
1629 	w1 = min(w1, amax);
1630 	w1 = max(w1, amin);
1631 	w2 = w - w1;
1632 	*wa = w1;
1633 	*wb = w2;
1634     }
1635 }
1636 
1637 /* Propose a layout for the layout PAIR into the W by H rectangle
1638  * whose top-left corner is at (X,Y), top-down.  The minimum and
1639  * maximum bounds for all pair-layouts must have been computed by
1640  * synthesize_bounds() before calling this function.  */
1641 static void
propose_layout(Layout * pair,Sint16 x,Sint16 y,Uint16 w,Uint16 h)1642 propose_layout(Layout *pair, Sint16 x, Sint16 y, Uint16 w, Uint16 h)
1643 {
1644     Layout *a = pair->a;
1645     Layout *b = pair->b;
1646     assert(w >= pair->minw);
1647     assert(h >= pair->minh);
1648     w = min(w, pair->maxw);
1649     h = min(h, pair->maxh);
1650     pair->p.r.x = x;
1651     pair->p.r.y = y;
1652     pair->p.r.w = w;
1653     pair->p.r.h = h;
1654     recompute_panel_view(&pair->p);
1655     if (!pair->a && !pair->b) {		/* A user's layout. */
1656 	return;				/* all done! */
1657     } /* else a pair layout */
1658 
1659     /* Choose sizes based on the split parameters. */
1660     if (is_horizontal_split(pair->method)) {
1661 	choose_sizes(w, pair->size,
1662 		     a->minw, a->maxw, b->minw, b->maxw,
1663 		     &a->p.r.w, &b->p.r.w);
1664 	a->p.r.h = b->p.r.h = h;
1665 	a->p.r.h = min(a->p.r.h, a->maxh); a->p.r.h = max(a->p.r.h, a->minh);
1666 	b->p.r.h = min(b->p.r.h, b->maxh); b->p.r.h = max(b->p.r.h, b->minh);
1667     } else {
1668 	choose_sizes(h, pair->size,
1669 		     a->minh, a->maxh, b->minh, b->maxh,
1670 		     &a->p.r.h, &b->p.r.h);
1671 	a->p.r.w = b->p.r.w = w;
1672 	a->p.r.w = min(a->p.r.w, a->maxw); a->p.r.w = max(a->p.r.w, a->minw);
1673 	b->p.r.w = min(b->p.r.w, b->maxw); b->p.r.w = max(b->p.r.w, b->minw);
1674     }
1675 
1676     /* Make sure the layout algorithm respects all bounds. */
1677     assert(a->p.r.w >= a->minw);
1678     assert(a->p.r.w <= a->maxw);
1679     assert(b->p.r.w >= b->minw);
1680     assert(b->p.r.w <= b->maxw);
1681     assert(a->p.r.h >= a->minh);
1682     assert(a->p.r.h <= a->maxh);
1683     assert(b->p.r.h >= b->minh);
1684     assert(b->p.r.h <= b->maxh);
1685     assert(a->p.r.w <= w);
1686     assert(b->p.r.w <= w);
1687     assert(a->p.r.h <= h);
1688     assert(b->p.r.h <= h);
1689     if (is_horizontal_split(pair->method)) {
1690 	assert(a->p.r.w + b->p.r.w <= w);
1691     } else {
1692 	assert(a->p.r.h + b->p.r.h <= h);
1693     }
1694 
1695     /* Set positions based on the method of split and sizes. */
1696     switch(pair->method) {
1697     case BELOWS: /* b below a */
1698 	a->p.r.x = x;
1699 	a->p.r.y = y;
1700 	b->p.r.x = x;
1701 	b->p.r.y = y + a->p.r.h;
1702 	break;
1703     case ABOVES: /* b above a */
1704 	b->p.r.x = x;
1705 	b->p.r.y = y;
1706 	a->p.r.x = x;
1707 	a->p.r.y = y + b->p.r.h;
1708 	break;
1709     case LEFTS:  /* b to left of a */
1710 	b->p.r.x = x;
1711 	b->p.r.y = y;
1712 	a->p.r.x = x + b->p.r.w;
1713 	a->p.r.y = y;
1714 	break;
1715     case RIGHTS: /* b to right of a */
1716 	a->p.r.x = x;
1717 	a->p.r.y = y;
1718 	b->p.r.x = x + a->p.r.w;
1719 	b->p.r.y = y;
1720 	break;
1721     }
1722     recompute_panel_view(&a->p);
1723     recompute_panel_view(&b->p);
1724 
1725     /* Propose layouts for children. */
1726     propose_layout(pair->a, a->p.r.x, a->p.r.y, a->p.r.w, a->p.r.h);
1727     propose_layout(pair->b, b->p.r.x, b->p.r.y, b->p.r.w, b->p.r.h);
1728 }
1729 
1730 /* Redraw all borders of layouts below (and including) layout A.  Calls
1731  * the doclear() callback of layouts. */
1732 static void
redraw_layout_borders(Layout * a)1733 redraw_layout_borders(Layout *a)
1734 {
1735     draw_border(&a->p.r, &a->p.b);
1736     if (a->a) { redraw_layout_borders(a->a); }
1737     if (a->b) { redraw_layout_borders(a->b); }
1738 }
1739 
1740 /* Clear all layouts below (and including) layout A.  Calls the
1741  * doclear() callback of layouts. */
1742 static void
clear_layout(Layout * a)1743 clear_layout(Layout *a)
1744 {
1745     if (a->doclear) { a->doclear(&a->p, a->parms); }
1746     if (a->a) { clear_layout(a->a); }
1747     if (a->b) { clear_layout(a->b); }
1748 }
1749 
1750 /* Call the doremode() call backs of layouts.  These should be called
1751  * whenever the display mode changes. */
1752 static void
remode_layout(Layout * a)1753 remode_layout(Layout *a)
1754 {
1755     if (a->a) { remode_layout(a->a); }
1756     if (a->b) { remode_layout(a->b); }
1757     if (a->doremode) { a->doremode(a->parms); }
1758 }
1759 
1760 /* Call the after_layout() call back of layouts.  The first one that
1761  * returns FALSE causes a quick return, so some call backs might not
1762  * be called. */
1763 static int
callback_after_layouts(Layout * a)1764 callback_after_layouts(Layout *a)
1765 {
1766     return (!a->a || callback_after_layouts(a->a))
1767 	&& (!a->b || callback_after_layouts(a->b))
1768 	&& (!a->after_layout || a->after_layout(&a->p, a->parms));
1769 }
1770 
1771 /* Call the before_layout() call backs of layouts below (and
1772  * including) A.  The first callback returning FALSE causes a quick
1773  * return, so some callbacks might not be called. */
1774 static int
callback_before_layouts(Layout * a,Uint16 w,Uint16 h)1775 callback_before_layouts(Layout *a, Uint16 w, Uint16 h)
1776 {
1777     return (!a->a || callback_before_layouts(a->a, w, h))
1778 	&& (!a->b || callback_before_layouts(a->b, w, h))
1779 	&& (!a->before_layout || a->before_layout(a, w, h));
1780 }
1781 
1782 /* Redraw a layout and all its children. */
1783 static void
redraw_layout(Layout * a)1784 redraw_layout(Layout *a)
1785 {
1786     if (a->doredraw) { (void)a->doredraw(&a->p, a->parms); }
1787     if (a->a) { redraw_layout(a->a); }
1788     if (a->b) { redraw_layout(a->b); }
1789 }
1790 
1791 /* Refresh the area on the display that the layout occupies. */
1792 static void
refresh_layout(Layout * a)1793 refresh_layout(Layout *a)
1794 {
1795     if (a->a) { refresh_layout(a->a); }
1796     if (a->b) { refresh_layout(a->b); }
1797     if (a->dorefresh) { (void)a->dorefresh(&a->p, a->parms); }
1798 }
1799 
1800 /* Attempt to recursively layout all the layouts below (and including)
1801  * the ROOT layout.  Returns TRUE on success, FALSE on failure. */
1802 static int
layout(Layout * root,Sint16 x,Sint16 y,Uint16 w,Uint16 h)1803 layout(Layout *root, Sint16 x, Sint16 y, Uint16 w, Uint16 h)
1804 {
1805     /* Let the user's layouts adjust themselves to the new width and height
1806      * of the display.
1807      */
1808     if (!callback_before_layouts(root, w, h)) {
1809 	return 0;
1810     }
1811 
1812     /* Check that we have enough space. */
1813     synthesize_bounds(root);
1814     if (w < root->minw || h < root->minh)
1815     {
1816 	return 0;
1817     }
1818 
1819     /* Wunderbar. Now compute a layout proposal into the layout structures
1820      * top-down. */
1821     propose_layout(root, x, y, w, h);
1822 
1823     /* Phew.  Now run the layout proposal past the user call-backs to see
1824      * if they agree. */
1825     return callback_after_layouts(root);
1826 }
1827 
1828 /* ------------------------------------------------------------------------
1829  * TextOutputs
1830  *
1831  * TextOutputs are Layouts that provide textual output in the current
1832  * font.  They have a cursor position, know how to wrap long lines
1833  * when necessary, and attempt to refresh only the parts of their
1834  * layout's panel that have been drawn on. */
1835 
1836 /* Return a TextOutput to the statically allocated pool of free TextOutputs. */
1837 static void
free_text_output(TextOutput * t)1838 free_text_output(TextOutput *t)
1839 {
1840     t->next = textoutput_free_list;
1841     textoutput_free_list = t;
1842 }
1843 
1844 /* Initialise the pool of free TextOutputs. */
1845 static void
init_all_text_outputs(void)1846 init_all_text_outputs(void)
1847 {
1848     static int done_init = 0;
1849     if (!done_init) {
1850 	int k;
1851 	textoutput_free_list = NULL;
1852 	for (k=MAXTEXTOUTPUTS-1; k>=0; --k) {
1853 	    free_text_output(&all_textoutputs[k]);
1854 	}
1855 	done_init = 1;
1856     }
1857 }
1858 
1859 #if 0
1860 static void
1861 print_rect(const char *msg, const SDL_Rect *r)
1862 {
1863     printf("%s %d,%d,%d,%d\n", msg, r->x, r->y, r->w, r->h);
1864 }
1865 #endif
1866 
1867 static int
doclear_textoutput(Panel * p,void * parms)1868 doclear_textoutput(Panel *p, void *parms)
1869 {
1870     TextOutput *t = (TextOutput*)parms;
1871     /* clear_panel(p); */
1872     clear_rect(&t->drawn);
1873 #if 0
1874     print_rect("cle", &t->drawn);
1875 #endif
1876     t->refresh = t->drawn;		/* Set refresh rectangle. */
1877     t->x = t->y = 0;			/* Set point. */
1878     t->drawn.x = p->r.x;
1879     t->drawn.y = p->r.y;
1880     t->drawn.w = t->drawn.h = 0;
1881     return 1;
1882 }
1883 
1884 static int
after_layout_textoutput(Panel * p,void * parms)1885 after_layout_textoutput(Panel *p, void *parms)
1886 {
1887     TextOutput *t = (TextOutput*)parms;
1888     t->refresh.w = t->refresh.h = 0;
1889     t->drawn = p->r;
1890     return 1;
1891 }
1892 
1893 static void
doclose_textoutput(void * parms)1894 doclose_textoutput(void *parms)
1895 {
1896     TextOutput *t = (TextOutput *)parms;
1897     free_text_output(t);
1898 }
1899 
1900 static int
dorefresh_textoutput(Panel * p,void * parms)1901 dorefresh_textoutput(Panel *p, void *parms)
1902 {
1903     TextOutput *t = (TextOutput *)parms;
1904     p=p;
1905     if (t->refresh.w && t->refresh.h) {
1906 #if 0
1907 	print_rect("ref", &t->refresh);
1908 #endif
1909 	refresh_rect(&t->refresh);
1910     }
1911     t->refresh = t->layout->p.r;
1912     t->refresh.w = t->refresh.h = 0;
1913     return 1;
1914 }
1915 
1916 static TextOutput *
alloc_text_output(Layout * a)1917 alloc_text_output(Layout *a)
1918 {
1919     TextOutput *t;
1920     init_all_text_outputs();
1921     t = textoutput_free_list;
1922     assert(t && "Ran out of text outputs");
1923     textoutput_free_list = t->next;
1924     memset((void*)t, 0, sizeof(TextOutput));
1925     t->layout = a;
1926     a->parms = t;
1927     a->after_layout = after_layout_textoutput;
1928     a->doclose = doclose_textoutput;
1929     a->doredraw = doclear_textoutput;
1930     a->doclear = doclear_textoutput;
1931     a->dorefresh = dorefresh_textoutput;
1932     t->x = t->y = 0;
1933     t->refresh.x = a->p.r.x;
1934     t->refresh.y = a->p.r.y;
1935     t->refresh.w = t->refresh.h = 0;
1936     t->drawn = t->refresh;
1937     return t;
1938 }
1939 
1940 static void
clear_to_eol(TextOutput * t)1941 clear_to_eol(TextOutput *t)
1942 {
1943     const SDL_Rect *r = panel_view(&t->layout->p);
1944     SDL_Rect v;
1945     v.x = r->x + t->x;
1946     v.y = r->y + t->y;
1947     v.h = CurrentFont.h;
1948     v.w = r->w;
1949     clip_rect(r, &v);
1950     Slock(TheSurf);
1951     SDL_FillRect(TheSurf, &v, Colours[BLACK]);
1952     Sunlock(TheSurf);
1953     refresh_rect(&v);
1954 }
1955 
1956 static void
cr(TextOutput * t)1957 cr(TextOutput *t)
1958 {
1959     const SDL_Rect *r = panel_view(&t->layout->p);
1960     t->x = 0;
1961     /* Do we need to scroll up? */
1962     if (t->y+2*CurrentFont.h <= r->h) {
1963 	/* No scrolling.  Move to the next line. */
1964 	t->y += CurrentFont.h;
1965 	clear_to_eol(t);
1966     } else {
1967 	/* Scroll the panel up so that at least one more line fits and
1968 	 * update the refresh rectangle to contain all of the panel.
1969 	 * */
1970 #if 0
1971 	/* This version scrolls the minimum amount necessary to allow
1972 	 * exactly one more line, but it turns out that that creates
1973 	 * jerky and ugly displays.  i.e. it attempts to keep the
1974 	 * bottom line aligned with the panel's bottom border.
1975 	 * */
1976 	int fh = CurrentFont.h;
1977 	int ofs1 = r->h - (t->y+fh);
1978 	int ofs2 = fh - ofs1;
1979 	SDL_Rect src, dst;
1980 
1981 	src = *r;			/* source rect */
1982 	src.y += ofs2;
1983 	src.h -= ofs2;
1984 	clip_rect(&t->drawn, &src);
1985 
1986 	dst = src;			/* dest rec. */
1987 	dst.y -= ofs2;
1988 
1989 	t->y = r->h - fh;		/* update point. */
1990 
1991 	SDL_BlitSurface(TheSurf, &src, TheSurf, &dst);
1992 	join_rect(&dst, &t->refresh);
1993 	join_rect(&dst, &t->drawn);
1994 #else
1995 	/* This version scrolls up enough so that the top line is
1996 	 * always aligned at the panel top border.  It leaves some unused
1997 	 * space at the bottom, but that's probably ok. */
1998 	int fh = CurrentFont.h;
1999 	SDL_Rect src, dst;
2000 	src = *r;
2001 	src.y += fh;
2002 	src.h -= fh;
2003 	clip_rect(&t->drawn, &src);
2004 	dst = src;
2005 	dst.y -= fh;
2006 
2007 	SDL_BlitSurface(TheSurf, &src, TheSurf, &dst);
2008 	join_rect(&dst, &t->refresh);
2009 	join_rect(&dst, &t->drawn);
2010 #endif
2011 	clear_to_eol(t);
2012     }
2013 }
2014 
2015 static void
emit(TextOutput * t,unsigned char c,int colix)2016 emit(TextOutput *t, unsigned char c, int colix)
2017 {
2018     const SDL_Rect *v = panel_view(&t->layout->p);
2019     if (c=='\n') { cr(t); return; }
2020 
2021     /* Do we need to wrap? */
2022     if (t->x+CurrentFont.w > v->w) {
2023 	cr(t);
2024     }
2025     {
2026 	SDL_Rect r;
2027 	r.x = v->x + t->x; r.y = v->y + t->y;
2028 	r.h = CurrentFont.h; r.w = CurrentFont.w;
2029 	clip_rect(v, &r);
2030 	outcharxy(c, r.x, r.y, colix, &r);
2031 	t->x += CurrentFont.w;
2032 	join_rect(&r, &t->refresh);
2033 	join_rect(&r, &t->drawn);
2034     }
2035 }
2036 
2037 static void
backspace(TextOutput * t)2038 backspace(TextOutput *t)
2039 {
2040     t->x -= CurrentFont.w;
2041     if (t->x<0) {
2042 	t->x = 0;
2043     }
2044 }
2045 
2046 static void
backchar(TextOutput * t)2047 backchar(TextOutput *t)
2048 {
2049     if (t->x >= CurrentFont.w) {
2050 	t->x -= CurrentFont.w;
2051     } else {
2052 	t->x = 0;
2053 	if (t->y >= CurrentFont.h) {
2054 	    int cols;
2055 	    t->y -= CurrentFont.h;
2056 	    panel_text_size(&t->layout->p, &cols, NULL);
2057 	    t->x = max(0,(cols-1))*CurrentFont.w;
2058 	}
2059     }
2060 }
2061 
2062 static void
delchar(TextOutput * t)2063 delchar(TextOutput *t)
2064 {
2065     backchar(t);
2066     emit(t, ' ', BLACK);
2067     backspace(t);
2068 }
2069 
2070 static void
type(TextOutput * t,const char * str,int colix)2071 type(TextOutput *t, const char *str, int colix)
2072 {
2073     while (*str) {
2074 	emit(t, *str, colix);
2075 	if (*str==' ' && t->x <= CurrentFont.w) {
2076 	    /* After wrapping text, don't emit a space as the very
2077 	       first character of a line. */
2078 	    backspace(t);
2079 	}
2080 	str++;
2081     }
2082 }
2083 
2084 
2085 static void
type_nowrap(TextOutput * t,const char * str,int colix)2086 type_nowrap(TextOutput *t, const char *str, int colix)
2087 {
2088     const SDL_Rect *v = panel_view(&t->layout->p);
2089     while (*str) {
2090 	SDL_Rect r;
2091 	r.x = v->x + t->x;
2092 	r.y = v->y + t->y;
2093 	r.w = CurrentFont.w;
2094 	r.h = CurrentFont.h;
2095 	clip_rect(v, &r);
2096 	outcharxy(*str, r.x, r.y, colix, &r);
2097 	t->x += CurrentFont.w;
2098 	join_rect(&r, &t->refresh);
2099 	join_rect(&r, &t->drawn);
2100 	str++;
2101     }
2102 }
2103 
2104 
2105 
2106 /* ------------------------------------------------------------------------
2107  * TextInputs
2108  *
2109  * TextInputs supplement Layouts by providing text input facilities.
2110  * They don't do any event handling themselves, but track the internal
2111  * state of a text panel that an event handler can modify through a
2112  * narrowish interface.  An event handler should always activate() a
2113  * TextInput when it is starting to gather input, and deactivate() it
2114  * when input has been gathered.  The handler is also responsible for
2115  * refreshing the TextInput's Layout at convenient moments by calling
2116  * the refresh_layout() function on it's layout.
2117  *
2118  * A TextInput has two states:
2119  *
2120  *  active : 		When set, the TextInput is all geared up for receiving
2121  *			characters from the user (event handler).  When clear,
2122  *			the TextInput pretends it is an empty panel.  This
2123  *			variable may only be modified by calls to activate(),
2124  *			deactivate(), and finish_input().
2125  *
2126  * and a handful of minor boolean state variables:
2127  *
2128  *  has_focus :         When set and the TextInput is active, draw a cursor
2129  *			character to indicate that to the user.  This variable
2130  *			is controlled through the functions give_focus() and
2131  *			take_focus().
2132  *
2133  *  needs_prompting:	If set, the next time the TextInput is activated,
2134  *			it will redraw the prompt and any input it has
2135  *			gathered into its input buffer.  This flag should be
2136  *			set at any time that the TextInput's visible area is
2137  *			externally modified (by resize events or whatever.)
2138  **/
2139 
2140 /* Return a TextInput to the statically allocated pool of free TextInputs. */
2141 static void
free_text_input(TextInput * t)2142 free_text_input(TextInput *t)
2143 {
2144     t->next = textinput_free_list;
2145     textinput_free_list = t;
2146 }
2147 
2148 /* Initialise the pool of free TextInputs. */
2149 static void
init_all_text_inputs(void)2150 init_all_text_inputs(void)
2151 {
2152     static int done_init = 0;
2153     if (!done_init) {
2154 	int k;
2155 	textinput_free_list = NULL;
2156 	for (k=MAXTEXTINPUTS-1; k>=0; --k) {
2157 	    free_text_input(&all_textinputs[k]);
2158 	}
2159 	done_init = 1;
2160     }
2161 }
2162 
2163 /* Draw the cursor in a TextInput if it is active and has focus. */
2164 static void
cursoron(TextInput * t)2165 cursoron(TextInput *t)
2166 {
2167     if (t->has_focus && t->active) {
2168 	emit(t->out, '_', t->colix);
2169 	backspace(t->out);
2170     }
2171 }
2172 
2173 /* Clear the cursor in a TextInput if it is active and has focus. */
2174 static void
cursoroff(TextInput * t)2175 cursoroff(TextInput *t)
2176 {
2177     if (t->has_focus && t->active) {
2178 	emit(t->out, ' ', t->colix);
2179 	backspace(t->out);
2180     }
2181 }
2182 
2183 /* Layout doredraw() callback redraws a text input if it is active. */
2184 static int
doredraw_textinput(Panel * p,void * parms)2185 doredraw_textinput(Panel *p, void *parms)
2186 {
2187     TextInput *t = (TextInput*)parms;
2188     doclear_textoutput(p, t->out);
2189     if (t->active) {
2190 	type(t->out, t->prompt, t->colix);
2191 	type(t->out, t->buf, t->colix);
2192 	cursoron(t);
2193     }
2194     return 1;
2195 }
2196 
2197 static int
after_layout_textinput(Panel * p,void * parms)2198 after_layout_textinput(Panel *p, void *parms)
2199 {
2200     TextInput *t = (TextInput*)parms;
2201     return after_layout_textoutput(p, t->out);
2202 }
2203 
2204 /* Layout dorefresh() callback refreshes the layout only when it has been
2205    drawn into. */
2206 static int
dorefresh_textinput(Panel * p,void * parms)2207 dorefresh_textinput(Panel *p, void *parms)
2208 {
2209     TextInput *t = (TextInput*)parms;
2210     dorefresh_textoutput(p, t->out);
2211     return 1;
2212 }
2213 
2214 
2215 /* Layout doclose() callback frees a related TextInput. */
2216 static void
doclose_textinput(void * parms)2217 doclose_textinput(void *parms)
2218 {
2219     TextInput *t = (TextInput*)parms;
2220     doclose_textoutput(t->out);
2221     free_text_input(t);
2222 }
2223 
2224 /* Allocate a new TextInput and relate it to the given Layout. */
2225 static TextInput *
alloc_text_input(Layout * a)2226 alloc_text_input(Layout *a)
2227 {
2228     TextInput *t;
2229     init_all_text_inputs();
2230     t = textinput_free_list;
2231     assert(t && "Ran out of text input panels");
2232     textinput_free_list = t->next;
2233     memset((void*)t, 0, sizeof(TextInput));
2234     t->out = alloc_text_output(a);
2235     t->colix = LIGHTGREY;
2236     t->layout = a;
2237     a->parms = t;
2238     a->doclose = doclose_textinput;
2239     a->doredraw = doredraw_textinput;
2240     a->doclear = doredraw_textinput;
2241     a->dorefresh = dorefresh_textinput;
2242     a->after_layout = after_layout_textinput;
2243     t->active = 0;
2244     t->has_focus = 0;
2245     t->needs_prompting = 1;
2246     return t;
2247 }
2248 
2249 /* Activate a TextInput for receiving input. */
2250 static void
activate(TextInput * t)2251 activate(TextInput *t)
2252 {
2253     if (t->needs_prompting) {
2254 	type(t->out, t->prompt, t->colix);
2255 	type(t->out, t->buf, t->colix);
2256     }
2257     t->active = 1;
2258     t->needs_prompting = 0;
2259     cursoron(t);
2260 }
2261 
2262 /* Deactivate a TextInput. */
2263 static void
deactivate(TextInput * t)2264 deactivate(TextInput *t)
2265 {
2266     t->active = 0;
2267 }
2268 
2269 /* Is the TextInput receiving input? */
2270 static int
is_active(TextInput * t)2271 is_active(TextInput *t)
2272 {
2273     return t->active;
2274 }
2275 
2276 /* Give focus. */
2277 static void
give_focus(TextInput * t)2278 give_focus(TextInput *t)
2279 {
2280     t->has_focus = 1;
2281     cursoron(t);
2282 }
2283 
2284 /* Remove focus. */
2285 static void
take_focus(TextInput * t)2286 take_focus(TextInput *t)
2287 {
2288     cursoroff(t);
2289     t->has_focus = 0;
2290 }
2291 
2292 /* Set the prompt string of a TextInput. */
2293 static void
set_prompt(TextInput * t,const char * prompt)2294 set_prompt(TextInput *t, const char *prompt)
2295 {
2296     xstrncpy(t->prompt, prompt, MAXALLCHAR);
2297 }
2298 
2299 /* Add a character to a TextInput. */
2300 static void
add_char(TextInput * t,char c)2301 add_char(TextInput *t, char c)
2302 {
2303     if (!is_active(t)) { return; }
2304     if (t->len+1 < MAXALLCHAR) {
2305 	t->buf[t->len++] = c;
2306 	t->buf[t->len] = 0;
2307 	if (c=='\n') { cursoroff(t); }	/* Remove lingering cursor. */
2308 	emit(t->out, c, t->colix);
2309 	if (c!='\n') { cursoron(t); }
2310     }
2311 }
2312 
2313 /* Erase the last character of a TextInput. */
2314 static void
del_char(TextInput * t)2315 del_char(TextInput *t)
2316 {
2317     if (!is_active(t)) { return; }
2318     if (t->len>0) {
2319 	t->buf[--t->len] = 0;
2320 	cursoroff(t);
2321 	delchar(t->out);
2322 	cursoron(t);
2323     }
2324 }
2325 
2326 /* Stop receiving input into the TextInput and fetch the input gathered
2327  * so far. */
2328 static void
finish_input(TextInput * t,char * buf,size_t maxbuf)2329 finish_input(TextInput *t, char *buf, size_t maxbuf)
2330 {
2331     t->buf[t->len] = 0;
2332     xstrncpy(buf, t->buf, maxbuf);
2333     cursoroff(t);
2334     deactivate(t);
2335     t->needs_prompting = 1;
2336     t->buf[0] = 0;
2337     t->len = 0;
2338 }
2339 
2340 /* Erase the input characters on the screen up to the prompt.  NB: This
2341  * does not throw away gathered input or deactivate the TextInput. */
2342 static void
hide_input(TextInput * t)2343 hide_input(TextInput *t)
2344 {
2345     size_t i;
2346     if (!is_active(t)) { return; }
2347     cursoroff(t);
2348     for (i=0; i<t->len; i++) {
2349 	delchar(t->out);
2350     }
2351     cursoron(t);
2352 }
2353 
2354 static void
reset_input(TextInput * t,const char * s)2355 reset_input(TextInput *t, const char *s)
2356 {
2357     if (!is_active(t)) { return; }
2358     hide_input(t);
2359     t->buf[0] = 0;
2360     t->len = 0;
2361     while (s && *s) { add_char(t, *s++); }
2362 }
2363 
2364 /* ------------------------------------------------------------------------
2365  * Core display layout
2366  */
2367 
2368 /* Return the size of a cell in pixels. */
2369 static Uint32
get_cell_size(const CellInfo * c)2370 get_cell_size(const CellInfo *c)
2371 {
2372     return 2*c->box_size + c->interbox_space + c->intercell_space;
2373 }
2374 
2375 /* Draw a cell-box at the given address in the given colour.  Bits TL
2376  * (top-left), TR (top-right), BL (bottom-left), BR (bottom-right) of BOXES
2377  * control which boxes are drawn in the cell.  Additionally it updates the
2378  * core colour buffer in the CoreArena. */
2379 static void
display_box(int addr,Uint32 boxes,int colix)2380 display_box(int addr, Uint32 boxes, int colix)
2381 {
2382     SDL_Rect dst;
2383     SDL_Surface *boxsurf;
2384 
2385     if (colix != 0) {
2386 	/* Colour core colour buffer and figure out which boxes need
2387 	 * colouring on the display. */
2388 #if 1
2389 	/* This version checks whether or not the box needs to be
2390 	 * blitted.  Doesn't work when resizing because then the boxes
2391 	 * always needs to be blitted so it's not used. */
2392 #define check_box(mask, ofs)\
2393 	if (boxes & (mask)) {\
2394 	    if (CoreArena.core[4*addr+(ofs)] != colix) {\
2395 		CoreArena.core[4*addr+(ofs)] = colix;\
2396 	    } else {\
2397 		boxes ^= (mask);\
2398 	    }\
2399 	}
2400 #else
2401 #define check_box(mask, ofs)\
2402 	if (boxes & (mask)) { CoreArena.core[4*addr+(ofs)] = colix; }
2403 #endif
2404 	check_box(TL, 0);
2405 	check_box(TR, 1);
2406 	check_box(BL, 2);
2407 	check_box(BR, 3);
2408 
2409 	/* If TL=0x00000001, TR=0x00000100, BL=0x00010000, BR=0x01000000
2410 	 * and CoreArena.core[] is an Uint32 array, then this might be faster
2411 	 * since only DWORD's are store-forwaded(?):
2412 	 *
2413 	 * CoreArena.core[addr] = (CoreArena.core[addr]
2414 	 *                         & ((boxes ^ (TL|TR|BL|BR)) * 0xFF)
2415 	 *                        )  |  (boxes * colix);
2416 	 */
2417 	if (boxes) {
2418 	    /* Set destination rectangle. */
2419 	    dst.x = xkoord(addr);
2420 	    dst.y = ykoord(addr);
2421 	    /* dst.w = dst.h = CellSize; */  /* ignored by SDL_BlitSurface().*/
2422 
2423 	    /* Get source surface. */
2424 	    boxsurf = BlitBoxes[colix][boxes];
2425 
2426 	    /* Blit it!  Note: requires colour keying to select area to blit.*/
2427 	    SDL_BlitSurface(boxsurf, NULL, TheSurf, &dst);
2428 	}
2429     }
2430 }
2431 
2432 /* Decrease the parameter cell size parameters a bit.  This is used to find a
2433  * suitable cell size that fits the screen.  Returns a flag that is zero when
2434  * the cell size can't be decreased any more, and if it can. */
2435 static int
decr_cell_size(CellInfo * c)2436 decr_cell_size(CellInfo *c)
2437 {
2438     int success=1;
2439     if (c->interbox_space > 1) {
2440 	/* Try to keep boxes further apart than one pixel. */
2441 	--c->interbox_space;
2442     } else if (c->intercell_space > 1) {
2443 	/* Try to keep cells further apart than boxes. */
2444 	--c->intercell_space;
2445     } else if (c->interbox_space > 0) {
2446 	/* Give in and make boxes touch each other. */
2447 	c->interbox_space = 0;
2448     } else if (c->intercell_space >= 1) {
2449 	/* Give in and make cells and boxes the same distance apart. */
2450 	c->intercell_space = 0;
2451     } else if (c->box_size>1) {
2452 	/* Try to keep boxes larger than one pixel */
2453 	--c->box_size;
2454 	if (c->intercell_space==0) {
2455 	    c->intercell_space = 1;
2456 	}
2457     } else {
2458 	assert(c->box_size == 1);
2459 	assert(c->interbox_space == 0);
2460 	assert(c->intercell_space == 0);
2461 	success = 0;
2462     }
2463     return success;
2464 }
2465 
2466 /* Compute the maximum and minimum height of the core display panel
2467  * based on the available width.  */
2468 static int
before_layout_arena(Layout * layout,Uint16 w,Uint16 h)2469 before_layout_arena(Layout *layout, Uint16 w, Uint16 h)
2470 {
2471     Panel *p = &layout->p;
2472     Arena *a = (Arena*)(layout->parms);
2473     Uint32 real_w = max(w, layout->minw) - abs(p->b.lft) - abs(p->b.rgt);
2474     Uint32 cellsperline;
2475 
2476     h = h;				/* unused. */
2477 
2478     cellsperline = real_w/2;		/* 2 = minimum cell size. */
2479     layout->minh = 2*(a->coresize + cellsperline-1)/cellsperline;
2480     layout->minh += abs(p->b.top) + abs(p->b.bot);
2481 
2482     layout->maxh = max(layout->maxh, layout->minh);
2483     return 1;
2484 }
2485 
2486 /* After the core panel has been laid out, find a cell size that will
2487  * fit into the alotted space.  Also set up related globals.  Provided
2488  * the panel is large enough, this should always succeed.  */
2489 static int
after_layout_arena(Panel * p,void * parms)2490 after_layout_arena(Panel *p, void *parms)
2491 {
2492     const SDL_Rect *r = panel_view(p);
2493     Arena *a = (Arena*)parms;
2494     int failed = 1;
2495     Uint32 rows, cellw, cellsperline;
2496 
2497     /* Find desired cell size. */
2498     a->cell = a->desired_cell;
2499     do {
2500 	cellw = get_cell_size(&a->cell);
2501 	cellsperline = r->w / cellw;
2502 	rows = (a->coresize + cellsperline-1)/cellsperline;
2503 #if 0
2504 	fprintf(stderr, "{ %d, %d, %d } = %d/%d\n", a->cell.box_size,
2505 		a->cell.interbox_space,	a->cell.intercell_space,
2506 		rows*cellw, r->h);
2507 #endif
2508 	if (rows * cellw <= r->h) {
2509 	    failed = 0;
2510 	}
2511     } while(failed && decr_cell_size(&a->cell));
2512 
2513 #if 0
2514 	fprintf(stderr, "{ %d, %d, %d } = %d/%d\n", a->cell.box_size,
2515 		a->cell.interbox_space,	a->cell.intercell_space,
2516 		rows*cellw, r->h);
2517 #endif
2518     /* Set up related globals. */
2519     ArenaX = r->x;
2520     ArenaY = r->y;
2521     ArenaX += (r->w - cellw*cellsperline + a->cell.intercell_space)/2;
2522     ArenaY += (r->h - cellw*rows + a->cell.intercell_space)/2;
2523     CellSize = get_cell_size(&a->cell);
2524     CellsPerLine = r->w/CellSize;
2525 
2526     /* If there's wasted horizontal space in the arena, adjust the
2527      * maximum height and fail this layout.  The layout algorithm
2528      * should try again and get it right. */
2529     if (r->h > rows*cellw + a->cell.intercell_space
2530 	     + abs(p->b.top) + abs(p->b.bot))
2531     {
2532 	a->layout->maxh = a->cell.intercell_space + rows*cellw;
2533 	a->layout->maxh += abs(p->b.top) + abs(p->b.bot);
2534 	a->layout->minh = min(a->layout->minh, a->layout->maxh);
2535 	failed = 1;
2536     }
2537 
2538     return !failed;
2539 }
2540 
2541 /* Layout doredraw() callback redraws the core panel from the core colour
2542  * buffer. */
2543 static int
doredraw_arena(Panel * p,void * parms)2544 doredraw_arena(Panel *p, void *parms)
2545 {
2546     Arena *a = (Arena*)parms;
2547     Uint32 addr;
2548     clear_panel(p);
2549     for (addr=0; addr<a->coresize; addr++) {
2550 	int i;
2551 	Uint32 mask = 1;
2552 	for (i=0; i<4; i++) {
2553 	    unsigned char col = a->core[4*addr+i];
2554 	    a->core[4*addr + i] = 0;
2555 	    display_box(addr, mask, col);
2556 	    mask <<= 1;
2557 	}
2558     }
2559     return 1;
2560 }
2561 
2562 /* Layout doclear() callback clears the arena and core colour buffer. */
2563 static int
doclear_arena(Panel * p,void * parms)2564 doclear_arena(Panel *p, void *parms)
2565 {
2566     Arena *a = (Arena*)parms;
2567     a = a;
2568     clear_panel(p);
2569     memset((void*)a->core, 0, sizeof(unsigned char)*4*a->coresize);
2570     return 1;
2571 }
2572 
2573 /* Free the blit boxes array. */
2574 static void
free_blitboxes(Arena * a)2575 free_blitboxes(Arena *a)
2576 {
2577     int colix, boxes;
2578     if (a->blitboxes==NULL) { return; }
2579     for (colix=0; colix<NCOLOURS; colix++) {
2580 	if (a->blitboxes[colix]) {
2581 	    for (boxes = 0; boxes < 16; boxes++) {
2582 		SDL_Surface *s = a->blitboxes[colix][boxes];
2583 		if (s) {
2584 		    SDL_FreeSurface(s);
2585 		    a->blitboxes[colix][boxes] = NULL;
2586 		}
2587 	    }
2588 	    FREE(a->blitboxes[colix]);
2589 	}
2590     }
2591     FREE(a->blitboxes);
2592     a->blitboxes = NULL;
2593 }
2594 
2595 /* Fill a square box of a surface with a colour. */
2596 static void
fill_box(SDL_Surface * s,Sint16 x,Sint16 y,Uint16 w,Uint16 h,Uint32 col)2597 fill_box(SDL_Surface *s, Sint16 x, Sint16 y, Uint16 w, Uint16 h, Uint32 col)
2598 {
2599     SDL_Rect r;
2600     r.x = x; r.y = y; r.w = w; r.h = h;
2601     if (-1==SDL_FillRect(s, &r, col)) {
2602 	exit_panic(errSpriteFill, SDL_GetError());
2603     }
2604 }
2605 
2606 /* Predraw a blitbox of a given colour and pattern. */
2607 static void
make_blitbox(Arena * a,int colix,Uint32 boxes)2608 make_blitbox(Arena *a, int colix, Uint32 boxes)
2609 {
2610     SDL_Surface *sprite, *temp;
2611     Uint32 key, col;
2612     int cellsize = get_cell_size(&a->cell);
2613     int bs = a->cell.box_size;
2614     int ibsp = a->cell.interbox_space;
2615 
2616     sprite = SDL_CreateRGBSurface(SDL_ANYFORMAT, cellsize, cellsize,
2617 				  TheSurf->format->BitsPerPixel,
2618 				  TheSurf->format->Rmask,
2619 				  TheSurf->format->Gmask,
2620 				  TheSurf->format->Bmask,
2621 				  0);	/* no alpha, we want colorkey blits */
2622     if (sprite == NULL) {
2623 	exit_panic(errSpriteSurf, SDL_GetError());
2624     }
2625 
2626     /* Set colorkey (=black) for the sprite. */
2627     key = SDL_MapRGB(sprite->format, 0, 0, 0);
2628     if (-1==SDL_SetColorKey(sprite, SDL_SRCCOLORKEY | SDL_RLEACCEL, key)) {
2629 	exit_panic(errSpriteColKey, SDL_GetError());
2630     }
2631 
2632 
2633     /* Draw the sprite. */
2634     col = SDL_MapRGB(sprite->format,
2635 		     PaletteRGB[colix].r,
2636 		     PaletteRGB[colix].g,
2637 		     PaletteRGB[colix].b);
2638     fill_box(sprite, 0, 0, cellsize, cellsize, key);
2639     if (boxes & TL) {
2640 	fill_box(sprite,       0,       0, bs, bs, col);
2641     }
2642     if (boxes & BL) {
2643 	fill_box(sprite,       0, bs+ibsp, bs, bs, col);
2644     }
2645     if (boxes & TR) {
2646 	fill_box(sprite, bs+ibsp,       0, bs, bs, col);
2647     }
2648     if (boxes & BR) {
2649 	fill_box(sprite, bs+ibsp, bs+ibsp, bs, bs, col);
2650     }
2651 
2652     /* Convert sprite to video format. */
2653     temp = SDL_DisplayFormat(sprite);
2654     SDL_FreeSurface(sprite);
2655     if (temp == NULL) {
2656        exit_panic(errSpriteConv, SDL_GetError());
2657     }
2658     sprite = temp;
2659 
2660     /* Save it for later! */
2661     a->blitboxes[colix][boxes] = sprite;
2662 }
2663 
2664 /* Predraw the blitboxes for a given colour. */
2665 static void
make_blitboxes_for_colix(Arena * a,int colix)2666 make_blitboxes_for_colix(Arena *a, int colix)
2667 {
2668     int i;
2669 
2670     /* Allocate the blitboxes[] array? */
2671     if (a->blitboxes == NULL) {
2672 	a->blitboxes = (SDL_Surface***)MALLOC(sizeof(SDL_Surface**)*NCOLOURS);
2673 	if (a->blitboxes==NULL) {
2674 	    exit_panic(outOfMemory, NULL);
2675 	}
2676 	for (i=0; i<NCOLOURS; i++) {
2677 	    a->blitboxes[i] = NULL;
2678 	}
2679     }
2680 
2681     /* Make the boxes for this colour index? */
2682     if (a->blitboxes[colix] == NULL) {
2683 	a->blitboxes[colix] = (SDL_Surface**)MALLOC(sizeof(SDL_Surface*)*16);
2684 	if (a->blitboxes[colix] == NULL) {
2685 	    exit_panic(outOfMemory, NULL);
2686 	}
2687 	for (i=0; i<16; i++) {
2688 	    make_blitbox(a, colix, i);
2689 	}
2690     }
2691 }
2692 
2693 
2694 /* Close the core panel. */
2695 static void
doclose_arena(void * parms)2696 doclose_arena(void *parms)
2697 {
2698     Arena *a = (Arena*)parms;
2699     free_blitboxes(a);
2700     if (a->core) {
2701 	FREE((void*)a->core);
2702 	a->core = NULL;
2703     }
2704 }
2705 
2706 /* Layout doremode() callback initiates the predraw of BlitBoxes used for fast
2707  * blitting of core cells. */
2708 static void
doremode_arena(void * parms)2709 doremode_arena(void *parms)
2710 {
2711     Arena *a = (Arena*)parms;
2712     int warix;
2713     free_blitboxes(a);
2714 
2715     for (warix=0; warix<warriors; warix++) {
2716 	make_blitboxes_for_colix(a, WarColourIx[warix]);
2717 	make_blitboxes_for_colix(a, DieColourIx[warix]);
2718     }
2719     BlitBoxes = a->blitboxes;
2720 }
2721 
2722 static CellInfo
choose_cell(int mode)2723 choose_cell(int mode)
2724 {
2725     mode = min(9, mode);
2726     mode = max(0, mode);
2727     return DesiredCells[mode];
2728 }
2729 
2730 
2731 /* Initialise the Layout LAYOUT to be the CoreArena for a core of size
2732  * CORESIZE. */
2733 static void
init_arena(Layout * layout,size_t coresize,int displayMode)2734 init_arena(Layout *layout, size_t coresize, int displayMode)
2735 {
2736     Arena *a = &CoreArena;
2737 
2738     a->coresize = coresize;
2739     a->core = (unsigned char*)MALLOC(sizeof(char)*4*coresize);
2740     if (!a->core) { exit_panic(outOfMemory, NULL); }
2741     memset((void*)a->core, 0, sizeof(unsigned char)*4*coresize);
2742 
2743     a->cell = choose_cell(displayMode);
2744 
2745     a->layout = layout;
2746     layout->parms = a;
2747     layout->before_layout = before_layout_arena;
2748     layout->after_layout = after_layout_arena;
2749     layout->doredraw = doredraw_arena;
2750     layout->doclear = doclear_arena;
2751     layout->doclose = doclose_arena;
2752     layout->doremode = doremode_arena;
2753 }
2754 
2755 /* ------------------------------------------------------------------------
2756  * Status line.
2757  */
2758 
2759 /* Layout doredraw() callback that draws the status line.  Depends on
2760  * global inCdb. */
2761 static int
doredraw_status(Panel * p,void * parms)2762 doredraw_status(Panel *p, void *parms)
2763 {
2764     TextOutput *t = (TextOutput *)parms;
2765     const SDL_Rect *r = panel_view(p);
2766     int white = WHITE;
2767     int red = RED;
2768     int yellow = YELLOW;
2769     int i;
2770 
2771     clear_panel(p);
2772     doclear_textoutput(p, parms);
2773 
2774     /* Speed level meter. */
2775     type_nowrap(t, "<", white);
2776     for (i=0; i<SPEEDLEVELS - displaySpeed; i++) {
2777 	if (t->x+CurrentFont.w+1 <= r->w) {
2778 	    outblankxy(r->x+t->x, r->y, red, r);
2779 	    t->x += CurrentFont.w+1;
2780 	}
2781     }
2782     for (i=0; i<displaySpeed; i++) {
2783 	if (t->x+CurrentFont.w <= r->w) {
2784 	    outblankxy(r->x+t->x+1, r->y, yellow, NULL);
2785 	    t->x += CurrentFont.w+1;
2786 	}
2787     }
2788     type_nowrap(t, ">", white);
2789 
2790     /* Detail level. */
2791     type_nowrap(t, "    ", white);
2792     for (i=0; i<5; i++) {
2793 	Uint32 col = i!=displayLevel ? white : red;
2794 	char s[10];
2795 	sprintf(s,"%d ", i);
2796 	type_nowrap(t, s, col);
2797     }
2798 
2799     /* Status */
2800     type_nowrap(t, "    ", white);
2801     if (inCdb) {
2802 	type_nowrap(t, "Debug", red);
2803     } else {
2804 	type_nowrap(t, "Debug space Quit", white);
2805     }
2806     return 1;
2807 }
2808 
2809 /* ------------------------------------------------------------------------
2810  * Warrior names and meters.
2811  */
2812 
2813 /* Layout doredraw() callback types the names of the warriors into the warrior
2814  * names layout. */
2815 static int
doredraw_names(Panel * p,void * parms)2816 doredraw_names(Panel *p, void *parms)
2817 {
2818     TextOutput *t = (TextOutput*)parms;
2819     const SDL_Rect *r = panel_view(p);
2820     int i;
2821 
2822     doclear_textoutput(p, parms);
2823     for (i=0; i<warriors; i++) {
2824 	t->y = 0;			/* space names evenly in panel. */
2825 	t->x = i*r->w/warriors;
2826 	type_nowrap(t, warrior[i].name, WarColourIx[i]);
2827     }
2828     return 1;
2829 }
2830 
2831 /* Redraw a meter that is at offset YOFS in the rectangle R, has a count of
2832  * COUNT, a maximum count of MAXCOUNT, and a pixels per count ratio of
2833  * RATIO. */
2834 static void
redraw_meter(const SDL_Rect * r,int yofs,int count,int maxcount,int ratio,Uint32 col)2835 redraw_meter(const SDL_Rect *r, int yofs, int count, int maxcount, int ratio,
2836 	     Uint32 col)
2837 {
2838     SDL_Rect q;
2839 
2840     Slock(TheSurf);
2841     putpixel(TheSurf, r->x + 1 + maxcount/ratio, r->y + yofs, col);
2842     Sunlock(TheSurf);
2843 
2844     q.x = r->x;  q.y = r->y + yofs;
2845     q.h = 1; q.w = 1 + count/ratio;
2846     SDL_FillRect(TheSurf, &q, col);
2847 }
2848 
2849 /* Layout doredraw() callback redraws the cycle and process meters. */
2850 static int
doredraw_meters(Panel * p,void * parms)2851 doredraw_meters(Panel *p, void *parms)
2852 {
2853     const SDL_Rect *r = panel_view(p);
2854     int i;
2855 
2856     parms = parms;
2857     clear_panel(p);
2858     for (i=0; i<warriors; i++) {
2859 	Uint32 col = Colours[WarColourIx[i]];
2860 	redraw_meter(r, splY[i]-r->y, warrior[i].tasks, taskNum,
2861 		     processRatio, col);
2862     }
2863 
2864     redraw_meter(r, cycleY-r->y, cycle, warriors*cycles,
2865 		 cycleRatio, Colours[WHITE]);
2866     return 1;
2867 }
2868 
2869 /* Layout after_layout() callback sets Y-coordinates and ratios of the various
2870  * meters. */
2871 static int
after_layout_meters(Panel * p,void * parms)2872 after_layout_meters(Panel *p, void *parms)
2873 {
2874     const SDL_Rect *r = panel_view(p);
2875     int i;
2876     parms = parms;
2877     for (i=0; i<warriors; i++) {
2878 	splY[i] = r->y + 2*i;
2879     }
2880     processRatio = (taskNum + r->w-2)/(r->w-1);
2881     processRatio = max(1, processRatio);
2882 
2883     MetersX = r->x;
2884     cycleY = r->y + 2*warriors+2;
2885     cycleRatio = (2*warriors*cycles + r->w-2)/(r->w-1);
2886     cycleRatio = max(1, cycleRatio);
2887     return 1;
2888 }
2889 
2890 /* ------------------------------------------------------------------------
2891  * Resize/Redraw
2892  */
2893 
2894 /* Redraw everything from scratch and update the display. */
2895 static void
redraw(void)2896 redraw(void)
2897 {
2898     Layout *root = get_root_layout(CoreLayout);
2899     redraw_layout(root);
2900     SDL_Flip(TheSurf);
2901 }
2902 
2903 /* Layout everything into a display of size W by H pixels, enlarging the
2904  * display if necessary.  This is the only function that calls set_VMode() to
2905  * open/resize the display, so it also initiates any one-time display-format
2906  * specific processing. */
2907 static void
relayout(Uint16 w,Uint16 h)2908 relayout(Uint16 w, Uint16 h)
2909 {
2910 #define BW 3
2911 #define BH 1
2912     Sint16 oldw = TheSurf ? TheVMode.w : -1;
2913     Sint16 oldh = TheSurf ? TheVMode.h : -1;
2914     Layout *root = get_root_layout(CoreLayout);
2915     CoreLayout->maxh = UNBOUNDED;
2916     CoreArena.desired_cell = choose_cell(displayMode);
2917 
2918     w = max(2*BW, w);
2919     h = max(2*BH, h);
2920 
2921     /* Attempt to compute a suitable layout. */
2922     if (!layout(root, BW, BH, w-2*BW, h-2*BH)) {
2923 	/* Failed with the desired width/height.  Try again by adjusting for
2924 	 * the minimum bounds. */
2925 	if (w <= root->minw+2*BW) { w = root->minw + 2*BW; }
2926 	if (h <= root->minh+2*BH) { h = root->minh + 2*BH; }
2927 
2928 	if (!layout(root, BW, BH, w-2*BW, h-2*BH)) {
2929 	    if (w <= root->minw+2*BW) { w = root->minw + 2*BW; }
2930 	    if (h <= root->minh+2*BH) { h = root->minh + 2*BH; }
2931 	    if (!layout(root, BW, BH, w-2*BW, h-2*BH)) {
2932 		/* Still no layout.  Bail. */
2933 		exit_panic("Couldn't compute a suitable layout", "BUG");
2934 	    }
2935 	}
2936     }
2937 
2938     if (!TheSurf || oldw != w || oldh != h) {
2939 	if (TheSurf) {
2940 	    SDL_FreeSurface(TheSurf);
2941 	    TheSurf = NULL;
2942 	}
2943 	TheVMode.w = w;
2944 	TheVMode.h = h;
2945 	TheSurf = set_VMode(&TheVMode);
2946 	if (!TheSurf) {
2947 	    exit_panic(errDisplayOpen, SDL_GetError());
2948 	}
2949     }
2950 #if 0
2951     printf("relayout ok\n");
2952 #endif
2953     remode_layout(root);
2954     redraw();
2955 }
2956 
2957 /* Initialise the global layout structures.  The display does not need to be
2958  * open. */
2959 static void
init_layout(void)2960 init_layout(void)
2961 {
2962     int i;
2963     Layout *names = NULL;
2964 
2965     /* Open arena layout. */
2966     CoreLayout = new_layout(200, UNBOUNDED,
2967 			    0, UNBOUNDED, border(2,2,2,2,WHITE));
2968     init_arena(CoreLayout, coreSize, displayMode);
2969 
2970     /* Open meters panel. */
2971     MetersLayout =
2972 	split_layout(CoreLayout, ABOVES, 0, 2*warriors+3, 2*warriors+3,
2973 		     border(-2,-2,-2,-2,WHITE));
2974     MetersLayout->minw = 2;
2975     MetersLayout->doredraw = doredraw_meters;
2976     MetersLayout->doclear = doredraw_meters;
2977     MetersLayout->after_layout = after_layout_meters;
2978 
2979     /* Open names panel. */
2980     if (warriors <= 2) {
2981 	names = split_layout(MetersLayout, ABOVES, 0,
2982 			     CurrentFont.h, CurrentFont.h,
2983 			     noborder);
2984 	names->parms = alloc_text_output(names);
2985 	names->doredraw = names->doclear = doredraw_names;
2986     }
2987 
2988     /* Open the status line panel. */
2989     StatusLayout = split_layout(CoreLayout, BELOWS, 0,
2990 				CurrentFont.h, CurrentFont.h, noborder);
2991     StatusLayout->parms = alloc_text_output(StatusLayout);
2992     StatusLayout->doredraw = doredraw_status;
2993     StatusLayout->doclear = doredraw_status;
2994 
2995     /* Initialise input panels.  We open an input panel that takes 0% of
2996      * the space of the core.  The layout algorithm will see to it that
2997      * whatever space is left over from the core will be given to the text
2998      * panels.
2999      */
3000     for (i=0; i<MAXTEXTINPUTS; i++) { TextPanels[i] = NULL; }
3001     TextPanels[0] =
3002 	alloc_text_input(
3003 	    split_layout(StatusLayout, BELOWS, 0, 2*CurrentFont.h, UNBOUNDED,
3004 			 noborder));
3005     set_prompt(TextPanels[0], CDB_PROMPT);
3006     give_focus(TextPanels[0]);
3007     NTextPanels = 1;
3008     curPanel = 1;
3009 
3010     /* Set warrior colours. */
3011     if (warriors <= 2) {
3012 	WarColourIx[0] = GREEN;
3013 	DieColourIx[0] = WarColourIx[0] % (NCOLOURS-1) + 1;
3014 	WarColourIx[1] = LIGHTRED;
3015 	DieColourIx[1] = WarColourIx[1] % (NCOLOURS-1) + 1;
3016     } else {
3017 	int i;
3018 	for (i=0; i<MAXWARRIOR; i++) {
3019 	    WarColourIx[i] = DieColourIx[i] = ((9-1+i) % (NCOLOURS-1)) + 1;
3020 	}
3021     }
3022 }
3023 
3024 /* ------------------------------------------------------------------------
3025  * Layout interface
3026  */
3027 
3028 /* Clear the core arena. */
3029 void
sdlgr_clear_arena(void)3030 sdlgr_clear_arena(void)
3031 {
3032     clear_layout(CoreLayout);
3033     refresh_layout(CoreLayout);
3034 }
3035 
3036 /* Clear the arena and redraw the meters. */
3037 void
sdlgr_display_clear(void)3038 sdlgr_display_clear(void)
3039 {
3040 
3041     clear_layout(CoreLayout);
3042     redraw_layout(MetersLayout);
3043     refresh_layout(CoreLayout);
3044     refresh_layout(MetersLayout);
3045 }
3046 
3047 /* Relayout everything. */
3048 void
sdlgr_relayout(void)3049 sdlgr_relayout(void)
3050 {
3051     relayout(TheVMode.w, TheVMode.h);
3052 }
3053 
3054 /* Refresh things on the screen. */
3055 void
sdlgr_refresh(int what)3056 sdlgr_refresh(int what)
3057 {
3058     switch (what) {
3059     case -1:
3060 	refresh_layout(get_root_layout(CoreLayout));
3061     case 0:
3062 	refresh_layout(CoreLayout);
3063 	break;
3064     default: {
3065 	    int i;
3066 	    for (i=0; i<NTextPanels; i++) {
3067 		refresh_layout(TextPanels[i]->layout);
3068 	    }
3069 	    break;
3070 	}
3071     }
3072 }
3073 
3074 /* Redraw the status line. */
3075 void
sdlgr_write_menu(void)3076 sdlgr_write_menu(void)
3077 {
3078     redraw_layout(StatusLayout);
3079     refresh_layout(StatusLayout);
3080 }
3081 
3082 /* ------------------------------------------------------------------------
3083  * Text Panels
3084  */
3085 
3086 /* Create new text panels by splitting an old one. */
3087 static int
split_text_panel(int panel,int size,Uint16 method)3088 split_text_panel(int panel, int size, Uint16 method)
3089 {
3090     assert(1 <= panel && panel <= NTextPanels);
3091     assert(TextPanels[panel-1]);
3092     size = min(100, size);
3093     size = max(0, size);
3094 
3095     if (NTextPanels < MAXTEXTINPUTS) {
3096 	Layout *a = TextPanels[panel-1]->layout;
3097 	Layout *b;
3098 	TextInput *t;
3099 	SDL_Rect r = a->p.r;
3100 	int newpanel;
3101 	b = split_layout(a,
3102 			 method, size,
3103 			 0, UNBOUNDED,
3104 			 noborder);
3105 	b->minh = CurrentFont.h;
3106 	b->minw = CurrentFont.w;
3107 
3108 	TextPanels[NTextPanels] = t = alloc_text_input(b);
3109 	set_prompt(t, CDB_PROMPT);
3110 	newpanel = ++NTextPanels;
3111 
3112 	/* Attempt to re-layout only the pair layout that contains the
3113 	 * new text panel. If it fails, then do a full re-layout.
3114 	 * This avoids having to redraw the arena on an update that
3115 	 * doesn't affect the core panel.  */
3116 	if (layout(a->parent, r.x, r.y, r.w, r.h)) {
3117 	    redraw_layout(b);
3118 	    refresh_layout(b);
3119 	} else {
3120 	    relayout(TheVMode.w, TheVMode.h);
3121 	}
3122 	return newpanel;
3123     }
3124     return -1;
3125 }
3126 
3127 /* Close a text panel. */
3128 static void
close_text_panel(int panel)3129 close_text_panel(int panel)
3130 {
3131     int i;
3132     SDL_Rect r;
3133     int need_redraw = 0;
3134     Layout *a, *b;
3135 
3136     if (panel < 1 || panel > NTextPanels) { return; }
3137     if (TextPanels[panel-1] == NULL) { return; }
3138     if (NTextPanels <= 1) { return; }
3139 
3140     a = TextPanels[panel-1]->layout;
3141     b = get_sibling_layout(a);
3142     r = a->parent->p.r;
3143     need_redraw = r.x != b->p.r.x || r.y != b->p.r.y;
3144     clear_layout(a);
3145     refresh_layout(a);
3146     close_layout(a);
3147     TextPanels[panel-1] = NULL;
3148 
3149     /* Shift the remaining panels down. */
3150     for (i=panel; i<NTextPanels; i++) {
3151 	TextPanels[i-1] = TextPanels[i];
3152     }
3153     --NTextPanels;
3154     TextPanels[NTextPanels] = NULL;
3155 
3156     if (layout(b, r.x, r.y, r.w, r.h)) {
3157 	if (need_redraw) {
3158 	    TextInput *t = (TextInput*)(b->parms);
3159 	    t->needs_prompting = 1;
3160 	    clear_layout(b);
3161 	    refresh_layout(b);
3162 	}
3163     } else {
3164 	relayout(TheVMode.w, TheVMode.h);
3165     }
3166 }
3167 
3168 /* Update the cdb text panels.  If NEXTPANEL == 0 then the current panel is
3169  * closed.  Otherwise, set focus to the panel NEXTPANEL (creating a new one if
3170  * NEWPANEL doesn't already exist).  The global curPanel is updated to reflect
3171  * the new panel.  NOTE: curPanel may be != NEXTPANEL if a new panel had to be
3172  * created, in which case curPanel is the id of the new panel. */
3173 void
sdlgr_update(int nextpanel)3174 sdlgr_update(int nextpanel)
3175 {
3176     /* Initialise? */
3177     if (curPanel <= 0) {
3178 	curPanel = 1;
3179 	assert(TextPanels[0]!=NULL);
3180     }
3181 
3182     /* Is this a close request? */
3183     if (nextpanel == 0) {
3184 	close_text_panel(curPanel);
3185 	nextpanel = curPanel <= NTextPanels ? curPanel : NTextPanels;
3186     } else {
3187 	take_focus(TextPanels[curPanel-1]);
3188 	/* Do we need to create a new panel? */
3189 	if (TextPanels[nextpanel-1] == NULL) {
3190 	    nextpanel = split_text_panel(NTextPanels, 50, RIGHTS);
3191 	    if (nextpanel<=0) {		/* split failed? */
3192 		return;			/* yup, give up. */
3193 	    }
3194 	}
3195     }
3196     curPanel = nextpanel;
3197     give_focus(TextPanels[curPanel-1]);
3198 }
3199 
3200 /* Clear the current panel. */
3201 void
sdlgr_clear(void)3202 sdlgr_clear(void)
3203 {
3204     Layout *a = TextPanels[curPanel-1]->layout;
3205     clear_layout(a);
3206 }
3207 
3208 /* Type a string to the current panel. */
3209 void
sdlgr_puts(const char * s)3210 sdlgr_puts(const char *s)
3211 {
3212     TextOutput *t = TextPanels[curPanel-1]->out;
3213     int colix = TextPanels[curPanel-1]->colix;
3214     if (printAttr) {
3215 	colix = WarColourIx[printAttr-1];
3216     }
3217     type(t, s, colix);
3218 }
3219 
3220 /* Get the number of rows in the current panel. */
3221 int
sdlgr_text_lines(void)3222 sdlgr_text_lines(void)
3223 {
3224     int rows;
3225     Panel *p = &TextPanels[curPanel-1]->layout->p;
3226     panel_text_size(p, NULL, &rows);
3227     return rows;
3228 }
3229 
3230 
3231 /* ------------------------------------------------------------------------
3232  * History ring
3233  *
3234  * This section provides command line history facilities for the event
3235  * handlers to use.
3236  **/
3237 
3238 static Cmd *
get_cmd_ring()3239 get_cmd_ring()
3240 {
3241     static int done_init = 0;
3242     if (!done_init) {
3243 	CmdRing.cmd = xstrdup("");
3244 	CmdRing.prev = CmdRing.next = &CmdRing;
3245 	done_init = 1;
3246     }
3247     return &CmdRing;
3248 }
3249 
3250 static void
add_cmd(const char * cmd)3251 add_cmd(const char *cmd)
3252 {
3253     Cmd *ring = get_cmd_ring();
3254     Cmd *c = MALLOC(sizeof(Cmd));
3255     if (c == NULL) {
3256 	exit_panic(outOfMemory, NULL);
3257     }
3258     c->cmd = xstrdup(cmd);
3259     { char *s = c->cmd; while (s && *s) { if (*s=='\n') { *s='\0'; } s++; } }
3260     c->next = ring->next;
3261     c->prev = ring;
3262     ring->next->prev = c;
3263     ring->next = c;
3264 }
3265 
3266 
3267 /* ------------------------------------------------------------------------
3268  * Keyboard and mouse input; event handlers.
3269  */
3270 #define any_set(bits,mask)  (((bits)&(mask))!=0)
3271 
3272 /* Handle VIDEORESIZE, VIDEOEXPOSE, and QUIT events. */
3273 static void
default_handler(SDL_Event * e)3274 default_handler(SDL_Event *e)
3275 {
3276 #ifdef WIN32
3277     /* Under windows we ignore the very first VIDEOEXPOSE event. */
3278     static int nvideoexpose_events = 0;
3279 #endif
3280     switch (e->type) {
3281     case SDL_VIDEORESIZE:
3282 	relayout(e->resize.w, e->resize.h);
3283 	SDL_Flip(TheSurf);
3284 	break;
3285     case SDL_VIDEOEXPOSE:
3286 #ifdef WIN32
3287 	if (++nvideoexpose_events > 1) {
3288 	    redraw();
3289 	}
3290 #endif
3291 	break;
3292     case SDL_QUIT:
3293 	sdlgr_display_close(WAIT);
3294 	Exit(USERABORT);
3295     }
3296 }
3297 
3298 /* Handle special key strokes: CTRL-C, CTRL-BREAK, ALT-RETURN.
3299  * Returns 1 if the key is handled, and 0 otherwise.  */
3300 static int
special_keyhandler(const SDL_keysym * s)3301 special_keyhandler(const SDL_keysym *s)
3302 {
3303     /* Process CTRL-C, CTRL-BREAK */
3304     if (any_set(s->mod, KMOD_CTRL)
3305 	&& (s->sym == SDLK_c
3306 	    || s->sym==SDLK_BREAK))
3307     {
3308 	SDL_Event e;
3309 	e.type = SDL_QUIT;
3310 	default_handler(&e);		/* shouldn't return. */
3311     }
3312 
3313     /* Toggle fullscreen mode on ALT-RETURN */
3314     if ((any_set(s->mod, KMOD_ALT)
3315 	 || any_set(s->mod, KMOD_MODE))
3316 	&& (s->sym == SDLK_RETURN))
3317     {
3318 	SDL_WM_ToggleFullScreen(TheSurf);
3319 	return 1;
3320     }
3321     return 0;
3322 }
3323 
3324 
3325 /** Deal with keypad and other conversion weirdness. */
3326 static void
normalise_keysym(SDL_keysym * s)3327 normalise_keysym(SDL_keysym *s)
3328 {
3329     int num = any_set(s->mod, KMOD_NUM);
3330 
3331     /* Windows (or perhaps SDL) doesn't handle keystrokes with AltGr too well.
3332      * Problems include: spurious modifier bits, iffy unicode conversion for
3333      * valid international keys, varying keysyms on different versions of windows,
3334      * and so on and so forth.  Ignoring all keys modified by AltGr doesn't work
3335      * well because some characters (e.g. @ $) can only be typed using AltGr
3336      * on scandinavian keyboards.
3337      */
3338     if (any_set(s->mod, KMOD_RALT)) {
3339 	s->mod = KMOD_RALT;
3340     }
3341 
3342     /* Keypad */
3343     switch (s->sym) {
3344     case SDLK_KP0:
3345 	if (num) { s->sym = s->unicode = '0'; } else { s->sym = SDLK_INSERT; }
3346 	break;
3347     case SDLK_KP1:
3348 	if (num) { s->sym = s->unicode = '1'; } else { s->sym = SDLK_END; }
3349 	break;
3350     case SDLK_KP2:
3351 	if (num) { s->sym = s->unicode = '2'; } else { s->sym = SDLK_DOWN; }
3352 	break;
3353     case SDLK_KP3:
3354 	if (num) { s->sym = s->unicode = '3'; } else { s->sym = SDLK_PAGEDOWN;}
3355 	break;
3356     case SDLK_KP4:
3357 	if (num) { s->sym = s->unicode = '4'; } else { s->sym = SDLK_LEFT; }
3358 	break;
3359     case SDLK_KP5:
3360 	if (num) { s->sym = s->unicode = '5'; }
3361 	break;
3362     case SDLK_KP6:
3363 	if (num) { s->sym = s->unicode = '6'; } else { s->sym = SDLK_RIGHT; }
3364 	break;
3365     case SDLK_KP7:
3366 	if (num) { s->sym = s->unicode = '7'; } else { s->sym = SDLK_HOME; }
3367 	break;
3368     case SDLK_KP8:
3369 	if (num) { s->sym = s->unicode = '8'; } else { s->sym = SDLK_UP; }
3370 	break;
3371     case SDLK_KP9:
3372 	if (num) { s->sym = s->unicode = '9'; } else { s->sym = SDLK_PAGEUP; }
3373 	break;
3374     }
3375 
3376 }
3377 
3378 /* Convert a key from a SDL_keysym structure to ASCII.  Returns 0 if
3379 * the key isn't an ASCII character, and the character otherwise. */
3380 static char
conv_key(const SDL_keysym * s)3381 conv_key(const SDL_keysym *s)
3382 {
3383     if (s->unicode >= 128) { return 0; } /* non-ASCII */
3384     if (any_set(s->mod, KMOD_CTRL | KMOD_LALT | KMOD_META)) {
3385 	return 0;		/* special, modified */
3386     }
3387     switch (s->sym) {
3388     case SDLK_RETURN:
3389     case SDLK_KP_ENTER:
3390 	return '\n';
3391     case SDLK_TAB:
3392 	return '\t';
3393     case SDLK_DELETE:
3394     case SDLK_BACKSPACE:
3395 	return '\b';
3396     case SDLK_ESCAPE:
3397 	return 27;			/* ASCII for escape. */
3398     default:
3399 	return s->unicode;
3400     }
3401     return 0;
3402 }
3403 
3404 static void
print_keysym(const SDL_keysym * s)3405 print_keysym(const SDL_keysym *s)
3406 {
3407     fprintf(stderr,"sym: %d, unicode: %d '%c', mod: %x\n",
3408 	    s->sym, s->unicode,
3409 	    isgraph(s->unicode) ? s->unicode : '.',
3410 	    s->mod);
3411 }
3412 
3413 
3414 /* Do special processing for macro keys: if the key is a modified
3415  * key or non-ASCII key (function key, arrow keys, etc.) return the
3416  * name of the key in the form " m <modifiers><keyname>\n" in the
3417  * buffer BUF.  Returns 1 if an expansion was made, and 0 if not. */
3418 static int
macro_key(const SDL_keysym * s,char * buf,int maxbuf)3419 macro_key(const SDL_keysym *s, char *buf, int maxbuf)
3420 {
3421     char name[MAXALLCHAR];
3422     int ok = 0;
3423     int modified = 0;
3424     static const size_t n = MAXALLCHAR;
3425     if (s->sym == SDLK_UNKNOWN) { return 0; }
3426 
3427     xstrncpy(name, " m ", n);
3428     if (any_set(s->mod, KMOD_CTRL)) {
3429 	xstrncat(name, "ctrl-", n); modified = 1;
3430     }
3431     if (any_set(s->mod, KMOD_LALT)) {
3432 	xstrncat(name, "alt-", n); modified = 1;
3433     }
3434     if (any_set(s->mod, KMOD_META)) {
3435 	xstrncat(name, "meta-", n); modified = 1;
3436     }
3437 
3438     switch (s->sym) {
3439     case SDLK_UP:
3440 	xstrncat(name, "up", n); ok=1;
3441 	break;
3442     case SDLK_DOWN:
3443 	xstrncat(name, "down", n); ok=1;
3444 	break;
3445     case SDLK_RIGHT:
3446 	xstrncat(name, "right", n); ok=1;
3447 	break;
3448     case SDLK_LEFT:
3449 	xstrncat(name, "left", n); ok=1;
3450 	break;
3451     case SDLK_INSERT:
3452 	xstrncat(name, "ins", n); ok=1;
3453 	break;
3454     case SDLK_HOME:
3455 	xstrncat(name, "home", n); ok=1;
3456 	break;
3457     case SDLK_END:
3458 	xstrncat(name, "end", n); ok=1;
3459 	break;
3460     case SDLK_PAGEUP:
3461 	xstrncat(name, "pgup", n); ok=1;
3462 	break;
3463     case SDLK_PAGEDOWN:
3464 	xstrncat(name, "pgdn", n); ok=1;
3465 	break;
3466     case SDLK_F1:    case SDLK_F2:    case SDLK_F3:    case SDLK_F4:
3467     case SDLK_F5:    case SDLK_F6:    case SDLK_F7:    case SDLK_F8:
3468     case SDLK_F9:    case SDLK_F10:    case SDLK_F11:    case SDLK_F12:
3469     case SDLK_F13:    case SDLK_F14:    case SDLK_F15:
3470 	{
3471 	    char keyname[5];
3472 	    sprintf(keyname, "f%d", 1+s->sym-SDLK_F1);
3473 	    xstrncat(name, keyname, n); ok=1;
3474 	    break;
3475 	}
3476     case SDLK_HELP:
3477 	xstrncat(name, "help", n); ok=1;
3478 	break;
3479 
3480     default:
3481 	if (modified) {
3482 	    if (isgraph(s->unicode)) {
3483 		xstrnapp(name, s->unicode, n); ok=1;
3484 	    }
3485 	    else if (s->sym >= 33 && s->sym < 128) {
3486 		xstrnapp(name, s->sym, n); ok=1;
3487 	    }
3488 	}
3489     }
3490     if (ok) {
3491 	xstrncat(name, "\n", n);
3492 	xstrncpy(buf, name, maxbuf);
3493     }
3494     return ok;
3495 }
3496 
3497 /* Get a string from a cdb panel and do special input processing. */
3498 char *
sdlgr_gets(char * buf,int maxbuf,const char * prompt)3499 sdlgr_gets(char *buf, int maxbuf, const char *prompt)
3500 {
3501     SDL_Event ev;
3502     SDL_Event *e = &ev;
3503     int done = 0;			/* got input? */
3504     int hide = 0;			/* if true, user input is overriden
3505 					   by a key/mouse macro */
3506     Cmd *curcmd = get_cmd_ring();
3507     int i;
3508 
3509     if (inputRedirection) {
3510 	return fgets(buf, maxbuf, stdin);
3511     }
3512 
3513     /* Set prompt for current panel. */
3514     assert(prompt);
3515     set_prompt(TextPanels[curPanel-1], prompt);
3516 
3517     /* Reactivate text panels. */
3518     for (i=0; i<NTextPanels; i++) {
3519 	activate(TextPanels[i]);
3520 	refresh_layout(TextPanels[i]->layout);
3521     }
3522 
3523     SDL_WarpMouse(xkoord(curAddr), ykoord(curAddr));
3524 
3525     /* Process events until we have input to pass back. */
3526     while (!done) {
3527 	TextInput *t;
3528 	while (0==SDL_WaitEvent(e)) {}	/* Get an event, ignore errors. */
3529 	t = TextPanels[curPanel-1];
3530 
3531 	switch (e->type) {
3532 	case SDL_MOUSEBUTTONDOWN:
3533 	    /* Expand mouse button macros. */
3534 	    switch (e->button.button) {
3535 	    case 1: xstrncpy(buf, " m mousel\n", maxbuf); done=hide=1; break;
3536 	    case 2: xstrncpy(buf, " m mousem\n", maxbuf); done=hide=1; break;
3537 	    case 3: xstrncpy(buf, " m mouser\n", maxbuf); done=hide=1; break;
3538 	    }
3539 	    /* Accept mouse input and update curAddr only if the click
3540 	     * is inside core. */
3541 	    if (done) {
3542 		int x = e->button.x;
3543 		int y = e->button.y;
3544 		if (x >= ArenaX && y >= ArenaY)
3545 		{
3546 		    int col = (x-ArenaX)/CellSize;
3547 		    int row = (y-ArenaY)/CellSize;
3548 		    int addr = col + row*CellsPerLine;
3549 		    if (col < CellsPerLine && addr < (int)coreSize) {
3550 			curAddr = addr;
3551 		    } else {
3552 			xstrncpy(buf, "", maxbuf);
3553 			done = hide = 0;
3554 		    }
3555 		}
3556 	    }
3557 	    break;
3558 
3559 	case SDL_KEYDOWN:
3560 	    /* Handle CTRL-C, CTRL-BREAK, ALT-RETURN, etc. */
3561 	    if (special_keyhandler(&e->key.keysym)) { break; }
3562 
3563 	    /* Possibly a history key? */
3564 	    if (any_set(e->key.keysym.mod, KMOD_SHIFT)) {
3565 		if (e->key.keysym.sym == SDLK_UP) {	/* SHIFT-UP */
3566 		    curcmd = curcmd->next;
3567 		    reset_input(t, curcmd->cmd);
3568 		    refresh_layout(t->layout);
3569 		    continue;		/* processing events. */
3570 		}
3571 		if (e->key.keysym.sym == SDLK_DOWN) { /* SHIFT-DOWN */
3572 		    curcmd = curcmd->prev;
3573 		    reset_input(t, curcmd->cmd);
3574 		    refresh_layout(t->layout);
3575 		    continue;		/* processing events. */
3576 		}
3577 	    }
3578 
3579 	    /* print_keysym(&e->key.keysym); */
3580 	    normalise_keysym(&e->key.keysym);
3581 	    /* Expand macro keys?  (F1, F2, ALT-K, etc.) */
3582 	    if (macro_key(&e->key.keysym, buf, maxbuf)) {
3583 		done = hide = 1;
3584 	    }
3585 	    else {
3586 		int c;
3587 		/* Key goes into the current text input panel. */
3588 		c = conv_key(&e->key.keysym);
3589 		switch (c) {
3590 		case 0:			/* Unknown key, ignore. */
3591 		    break;
3592 		case '\b':		/* DEL, BACKSPACE erases a character.*/
3593 		    del_char(t);
3594 		    refresh_layout(t->layout);
3595 		    break;
3596 		case '\t':		/* TAB moves focus to next panel. */
3597 		    take_focus(t);
3598 		    refresh_layout(t->layout);
3599 		    curPanel = 1 + (curPanel-1 + 1) % NTextPanels;
3600 		    t = TextPanels[curPanel-1];
3601 		    give_focus(t);
3602 		    refresh_layout(t->layout);
3603 		    break;
3604 		case '\n':		/* RETURN, ENTER finishes input. */
3605 		    add_char(t, c);
3606 		    finish_input(t, buf, maxbuf);
3607 		    done = 1;
3608 		    break;
3609 		default:		/* It's an ASCII char; add it. */
3610 		    if (isgraph(c) || isspace(c)) {
3611 			add_char(t, c);
3612 			refresh_layout(t->layout);
3613 			break;
3614 		    }
3615 		}
3616 	    }
3617 	    break;
3618 	    /* The default handler handles window resize, quit, and
3619 	       expose events */
3620 	default:
3621 	    default_handler(e);
3622 	}
3623     }
3624 
3625     /* If user input was overriden by a key/mouse macro, show the
3626      * expansion text as if the user had input it (but don't forget
3627      * what the user was composing.)  Also, don't enter the expansion
3628      * text into the history ring.  */
3629     if (hide) {
3630 	TextInput *t = TextPanels[curPanel-1];
3631 	t->needs_prompting = 1;		/* Forces redraw of prompt and
3632 					   the interrupted input line
3633 					   next time the panel is activated.
3634 					*/
3635 	hide_input(t);
3636 	type(t->out, buf, t->colix);
3637     } else {
3638 	/* Add user input to the history ring if it's not empty. */
3639 	char *s = buf;
3640 	while (s && isspace(*s)) { s++; }
3641 	if (s && *s!=0) {
3642 	    add_cmd(buf);
3643 	}
3644     }
3645 
3646     /* Deactivate the text panels so they don't redraw the prompt when
3647      * they are resized/exposed.  */
3648     for (i=0; i<NTextPanels; i++) {
3649 	deactivate(TextPanels[i]);
3650 	refresh_layout(TextPanels[i]->layout);
3651     }
3652 
3653     return buf;
3654 }
3655 
3656 
3657 /* ------------------------------------------------------------------------
3658  * Core display interface
3659  */
3660 
3661 void
sdlgr_set_displayMode(int newmode)3662 sdlgr_set_displayMode(int newmode)
3663 {
3664     newmode = min(9, newmode);
3665     newmode = max(0, newmode);
3666     if (newmode != displayMode) {
3667 	displayMode = newmode;
3668 	if (TheSurf) {
3669 	    sdlgr_relayout();
3670 	}
3671     }
3672 }
3673 
3674 void
sdlgr_set_displaySpeed(int newspeed)3675 sdlgr_set_displaySpeed(int newspeed)
3676 {
3677     newspeed = max(newspeed, 0);
3678     displaySpeed = min(newspeed, SPEEDLEVELS-1);
3679     keyDelay = keyDelayAr[displaySpeed];
3680     keyDelay = min(keyDelay, cycles>1 ? cycles-1 : 1);
3681     if (displayLevel == 0) {
3682 	keyDelay = 10000;
3683     }
3684 }
3685 
3686 void
sdlgr_set_displayLevel(int newlevel)3687 sdlgr_set_displayLevel(int newlevel)
3688 {
3689     newlevel = min(4, newlevel);
3690     displayLevel = max(newlevel, 0);
3691     sdlgr_set_displaySpeed(displaySpeed);
3692 }
3693 
3694 static void
sdlgr_display_cycle()3695 sdlgr_display_cycle()
3696 {
3697     if (cycle % cycleRatio == 0) {
3698 	Sint16 x = MetersX + cycle/cycleRatio;
3699 	Slock(TheSurf);
3700 	putpixel(TheSurf, x, cycleY, Colours[BLACK]);
3701 	Sunlock(TheSurf);
3702 	/*SDL_UpdateRect(TheSurf, x, cycleY, 1, 1);*/
3703     }
3704 
3705     /* If it's time to check for keys and refresh the display, do so. */
3706     if (cycle % keyDelay == 0) {
3707 	SDL_Event e;
3708 	int key = 0;
3709 
3710 	/* Check for key presses and display changes. */
3711 	if (SDL_PollEvent(&e)) {
3712 	    switch (e.type) {
3713 	    case SDL_KEYDOWN:
3714 		if (!special_keyhandler(&e.key.keysym)) {
3715 		    key = conv_key(&e.key.keysym);
3716 		}
3717 		break;
3718 	    default:
3719 		default_handler(&e);
3720 	    }
3721 
3722 	}
3723 	if (key && !inputRedirection) {
3724 	    switch (key) {
3725 	    case ' ':
3726 	    case 'r':
3727 	    case 'R':
3728 		sdlgr_clear_arena();
3729 		break;
3730 
3731 	    case '>':
3732 		sdlgr_set_displaySpeed(displaySpeed-1);
3733 		break;
3734 
3735 	    case '<':
3736 		sdlgr_set_displaySpeed(displaySpeed+1);
3737 		break;
3738 
3739 	    case 27:			/* ASCII for escape. */
3740 	    case 'Q':
3741 	    case 'q':
3742 		e.type = SDL_QUIT;
3743 		default_handler(&e);
3744 		break;
3745 
3746 	    case '0': sdlgr_set_displayLevel(0); break;
3747 	    case '1': sdlgr_set_displayLevel(1); break;
3748 	    case '2': sdlgr_set_displayLevel(2); break;
3749 	    case '3': sdlgr_set_displayLevel(3); break;
3750 	    case '4': sdlgr_set_displayLevel(4); break;
3751 
3752 	    default:
3753 		sighandler(0);	/* terminate macros in progress, enter
3754                                    debug state. */
3755 		break;
3756 	    }
3757 	    sdlgr_write_menu();
3758 	}
3759 	refresh_layout(CoreLayout);
3760 	refresh_layout(MetersLayout);
3761     }
3762 }
3763 
3764 #define display_die(warnum)
3765 #define display_push(warnum)
3766 #define display_init() sdlgr_open_graphics()
3767 #define display_clear() sdlgr_display_clear()
3768 #define display_close() sdlgr_display_close(WAIT)
3769 #define display_cycle() sdlgr_display_cycle()
3770 
3771 #define display_read(addr) \
3772     do {\
3773 	if (displayLevel > 3)\
3774 	    display_box((addr), TL, WarColourIx[W-warrior]);\
3775     } while (0)
3776 
3777 #define display_write(addr) \
3778     do {\
3779 	if (displayLevel > 1)\
3780 	    display_box((addr), TR|BL, WarColourIx[W-warrior]);\
3781     } while (0)
3782 
3783 #define display_dec(addr) \
3784     do {\
3785 	if (displayLevel > 2)\
3786 	    display_box((addr), TL|TR, WarColourIx[W-warrior]);\
3787     } while (0)
3788 
3789 #define display_inc(addr) \
3790     do {\
3791 	if (displayLevel > 2)\
3792 	    display_box((addr), TL|BL, WarColourIx[W-warrior]);\
3793     } while (0)
3794 
3795 #define display_exec(addr) \
3796     do {\
3797 	if (displayLevel > 0)\
3798 	    display_box((addr), TL|TR|BL|BR, WarColourIx[W-warrior]);\
3799     } while (0)
3800 
3801 #define display_spl(warNum, tasks) \
3802     do {\
3803 	int __w = (warNum);\
3804 	Sint16 x = MetersX + (tasks)/processRatio;\
3805 	Sint16 y = splY[__w];\
3806         Slock(TheSurf);\
3807 	putpixel(TheSurf, x, y, Colours[WarColourIx[__w]]);\
3808         Sunlock(TheSurf);\
3809 	/* SDL_UpdateRect(TheSurf, x, y, 1, 1); */\
3810     } while (0)
3811 
3812 #define display_dat(addr, warNum, tasks) \
3813     do {\
3814 	int __w = (warNum);\
3815 	int x = MetersX + (tasks)/processRatio;\
3816 	int y = splY[__w];\
3817 	if (displayLevel > 0)\
3818 	    display_box((addr), TL|TR|BL|BR, DieColourIx[__w]);\
3819         Slock(TheSurf);\
3820 	putpixel(TheSurf, x, y, Colours[BLACK]);\
3821         Sunlock(TheSurf);\
3822 	/* SDL_UpdateRect(TheSurf, x, y, 1, 1); */\
3823     } while (0)
3824 
3825 /* ------------------------------------------------------------------------
3826  * Graphics display open/close.
3827  */
3828 
3829 /* atexit() hook that quits SDL. */
3830 static void
exit_hook(void)3831 exit_hook(void)
3832 {
3833     static int has_quit = 0;
3834     if (has_quit) { return; }
3835 
3836     has_quit = 1;
3837     if (TheSurf) {
3838 	SDL_FreeSurface(TheSurf);
3839 	TheSurf = NULL;
3840     }
3841     SDL_Quit();
3842 }
3843 
3844 /* Called by pMARS to initialise the SDL graphics subsystem. */
3845 void
sdlgr_open_graphics(void)3846 sdlgr_open_graphics(void)
3847 {
3848     const char *modestr;
3849 
3850     /* Initialise SDL. */
3851     atexit(exit_hook);
3852     if (SDL_Init(SDL_INIT_VIDEO)) {
3853 	exit_panic(failedSDLInit, SDL_GetError());
3854     }
3855     TheSurf = NULL;
3856     SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
3857     SDL_EnableUNICODE(1);
3858 
3859     /* Decode -mode argument. */
3860     modestr = sdlgr_Storage[0] ? sdlgr_Storage[0] : "";
3861     if ((modestr = decode_vmode_string(&TheVMode, modestr))) {
3862 	exit_panic(badModeString, modestr);
3863     }
3864 
3865     /* Preinitialise the font. (= clear it out with NULLs.)*/
3866     Font_Init(&CurrentFont, NULL, 8, 16, 128);
3867 
3868     /* Set up the layout. */
3869     init_layout();
3870     relayout(TheVMode.w, TheVMode.h);
3871 }
3872 
3873 /* Called by pMARS to close the graphics subsystem.  If WAIT is non-zero, then
3874  * prints a message waits for a keypress before shutting down. */
3875 void
sdlgr_display_close(int wait)3876 sdlgr_display_close(int wait)
3877 {
3878     SDL_Event e;
3879     if (wait == WAIT) {
3880 	sdlgr_puts(pressAnyKey);
3881 	sdlgr_refresh(curPanel);
3882     }
3883     while (wait == WAIT) {
3884 	if (SDL_WaitEvent(&e)) {
3885 	    switch (e.type) {
3886 	    case SDL_KEYDOWN:
3887 		wait = NOWAIT; break;
3888 	    default:
3889 		default_handler(&e);
3890 	    }
3891 	}
3892     }
3893     exit_hook();
3894 }
3895