1 //
2 // nazghul - an old-school RPG engine
3 // Copyright (C) 2002, 2003 Gordon McNutt
4 //
5 // This program is free software; you can redistribute it and/or modify it
6 // under the terms of the GNU General Public License as published by the Free
7 // Software Foundation; either version 2 of the License, or (at your option)
8 // any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but WITHOUT
11 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
13 // more details.
14 //
15 // You should have received a copy of the GNU General Public License along with
16 // this program; if not, write to the Free Foundation, Inc., 59 Temple Place,
17 // Suite 330, Boston, MA 02111-1307 USA
18 //
19 // Gordon McNutt
20 // gmcnutt@users.sourceforge.net
21 //
22 
23 #include "cmdwin.h"
24 
25 #include "cfg.h"
26 #include "common.h"
27 #include "console.h"
28 #include "dimensions.h"
29 #include "images.h"
30 #include "log.h"
31 #include "screen.h"
32 #include "sprite.h"
33 
34 #include <assert.h>
35 #include <errno.h>
36 #include <stdarg.h>
37 
38 #define CMDWIN_FRAG_MAX_LEN 64
39 #define CMDWIN_BUF_SZ       256
40 
41 /* Fragment flags */
42 #define CMDWIN_FRAG_SEP     (1<<0)
43 #define CMDWIN_FRAG_MARK    (1<<1)
44 
45 struct cmdwin_frag {
46         struct list list;
47         int flags;
48         char buf[CMDWIN_FRAG_MAX_LEN];
49 };
50 
51 static struct {
52 	SDL_Rect srect; /* screen rectangle (pixels) */
53 	char *buf;      /* string buffer */
54 	char *ptr;      /* next empty spot in buffer */
55 	int blen;       /* buffer length, this should be bigger than slen and
56                          * is the max expected total size of any prompt (the
57                          * longest prompts may be too big for the window) */
58 	int room;       /* empty space in buffer */
59         int slen;       /* printable string length (blen >= slen), this is
60                          * limited by the cmdwin UI size */
61         struct sprite *cursor_sprite;
62         struct list frags;
63 } cmdwin;
64 
65 #ifdef DEBUG
66 static FILE *log = NULL;
67 #endif
68 
cmdwin_clear_no_repaint()69 static inline void cmdwin_clear_no_repaint()
70 {
71 	memset(cmdwin.buf, 0, cmdwin.blen);
72 	cmdwin.ptr = cmdwin.buf;
73 	cmdwin.room = cmdwin.blen;
74 }
75 
cmdwin_cursor_sprite_init()76 static void cmdwin_cursor_sprite_init()
77 {
78         char *fname = cfg_get("cursor-image-filename");
79         struct images *ss_cursor = 0;
80 
81         assert(fname);
82         ss_cursor = images_new(0, 8, 16, 1, 4, 0, 0, fname);
83         assert(ss_cursor);
84         cmdwin.cursor_sprite = sprite_new(0, 4, 0, 0, 0, ss_cursor);
85         assert(cmdwin.cursor_sprite);
86 }
87 
cmdwin_clear_frag_stack(void)88 static void cmdwin_clear_frag_stack(void)
89 {
90         struct list *entry;
91 
92         entry = cmdwin.frags.next;
93         while (entry != &cmdwin.frags) {
94                 struct cmdwin_frag *frag = (struct cmdwin_frag*)entry;
95                 entry = entry->next;
96                 list_remove(&frag->list);
97                 free(frag);
98         }
99 }
100 
cmdwin_top()101 static struct cmdwin_frag *cmdwin_top()
102 {
103         if (list_empty(&cmdwin.frags))
104                 return 0;
105         return (struct cmdwin_frag*)cmdwin.frags.prev;
106 }
107 
cmdwin_reprint_buffer(void)108 static void cmdwin_reprint_buffer(void)
109 {
110         struct list *entry;
111 
112         /* Erase the buffer */
113         cmdwin_clear_no_repaint();
114 
115         /* Loop over the fragments until out of room or out of fragments */
116         list_for_each(&cmdwin.frags, entry) {
117                 struct cmdwin_frag *frag = (struct cmdwin_frag*)entry;
118                 int n = 0;
119 
120                 /* Append the fragment to the buffer. */
121                 if ((frag->flags & CMDWIN_FRAG_SEP)
122                     && (entry->next != &cmdwin.frags)) {
123                         /* Print a '-' after this fragment. */
124                         n = snprintf(cmdwin.ptr, cmdwin.room, "%s-", frag->buf);
125                 } else {
126                         /* No '-' afterwards. */
127                         n = snprintf(cmdwin.ptr, cmdwin.room, "%s", frag->buf);
128                 }
129                 n = min(n, cmdwin.room);
130                 cmdwin.room -= n;
131                 cmdwin.ptr += n;
132 
133                 /* If out of room then stop, and backup the ptr to the last
134                  * entry in the buffer */
135                 if (!cmdwin.room) {
136                         cmdwin.ptr--;
137                         break;
138                 }
139 
140         }
141 }
142 
cmdwin_init(void)143 int cmdwin_init(void)
144 {
145         cmdwin_cursor_sprite_init();
146 
147         list_init(&cmdwin.frags);
148 
149 	cmdwin.srect.x = CMD_X;
150 	cmdwin.srect.y = CMD_Y;
151 	cmdwin.srect.w = CMD_W;
152 	cmdwin.srect.h = CMD_H;
153 	cmdwin.slen = (CMD_W / ASCII_W) - 1; /* leave one space for the
154                                               * cursor */
155         cmdwin.blen = CMDWIN_BUF_SZ;
156         assert(cmdwin.blen >= cmdwin.slen);
157 
158 	cmdwin.buf = (char *) malloc(cmdwin.blen);
159 	if (!cmdwin.buf)
160 		return -1;
161 
162 #ifdef DEBUG
163 	log = fopen(".cmdwin", "w+");
164 	if (!log) {
165 		err(strerror(errno));
166 		return -1;
167 	}
168 #endif
169 
170         cmdwin_clear_no_repaint();
171 	return 0;
172 }
173 
cmdwin_vpush(int flags,const char * fmt,va_list args)174 static void cmdwin_vpush(int flags, const char *fmt, va_list args)
175 {
176         /* Allocate a new fragment */
177         struct cmdwin_frag *frag = (struct cmdwin_frag*)malloc(sizeof(*frag));
178         if (!frag) {
179                 warn("allocation failed");
180                 return;
181         }
182 
183         frag->flags = flags;
184 
185         /* default to empty string */
186         frag->buf[0] = 0;
187 
188         /* Store the string in the fragment */
189         if (fmt != NULL) {
190                 vsnprintf(frag->buf, sizeof(frag->buf), fmt, args);
191         }
192 
193         /* Push the fragment onto the stack */
194         list_add_tail(&cmdwin.frags, &frag->list);
195 
196         /* Reprint the buffer with the new fragment */
197         cmdwin_reprint_buffer();
198 
199         /* Update the display */
200 	cmdwin_repaint();
201 }
202 
cmdwin_spush(const char * fmt,...)203 void cmdwin_spush(const char *fmt, ...)
204 {
205 	va_list args;
206 
207 	va_start(args, fmt);
208         cmdwin_vpush(CMDWIN_FRAG_SEP, fmt, args);
209 	va_end(args);
210 
211 }
212 
cmdwin_push(const char * fmt,...)213 void cmdwin_push(const char *fmt, ...)
214 {
215 	va_list args;
216 
217 	va_start(args, fmt);
218         cmdwin_vpush(0, fmt, args);
219 	va_end(args);
220 }
221 
cmdwin_push_mark()222 void cmdwin_push_mark()
223 {
224         cmdwin_vpush(CMDWIN_FRAG_MARK, 0, 0);
225 }
226 
cmdwin_pop(void)227 void cmdwin_pop(void)
228 {
229         struct cmdwin_frag *frag;
230 
231         /* Fragment stack should not be empty. */
232         assert(! list_empty(&cmdwin.frags));
233 
234         /* Remove the last fragment and free it. */
235         frag = (struct cmdwin_frag*)cmdwin.frags.prev;
236         list_remove(&frag->list);
237         free(frag);
238 
239         /* Reprint the buffer without the fragment */
240         cmdwin_reprint_buffer();
241 
242         /* Update the display */
243 	cmdwin_repaint();
244 }
245 
cmdwin_pop_to_mark()246 void cmdwin_pop_to_mark()
247 {
248         struct cmdwin_frag *frag = cmdwin_top();
249         while (frag && frag->flags != CMDWIN_FRAG_MARK) {
250                 cmdwin_pop();
251                 frag = cmdwin_top();
252         }
253 
254         /* DON'T pop the mark itself */
255 }
256 
cmdwin_clear(void)257 void cmdwin_clear(void)
258 {
259         cmdwin_clear_frag_stack();
260         cmdwin_clear_no_repaint();
261         cmdwin_repaint();
262 }
263 
cmdwin_repaint_cursor(void)264 void cmdwin_repaint_cursor(void)
265 {
266 	SDL_Rect rect;
267 
268 	rect.x = cmdwin.srect.x;
269 	rect.y = cmdwin.srect.y;
270 	rect.w = ASCII_W;
271 	rect.h = ASCII_H;
272 
273         /* If the string is too big, show the last part of it (in other words,
274          * right-justify it) */
275         char *start = max(cmdwin.buf, cmdwin.ptr - cmdwin.slen);
276 	rect.x += (cmdwin.ptr - start) * ASCII_W;
277 
278 	sprite_paint(cmdwin.cursor_sprite, 0, rect.x, rect.y);
279 	screenUpdate(&rect);
280 }
281 
cmdwin_repaint(void)282 void cmdwin_repaint(void)
283 {
284         /* If the string is too big, show the last part of it (in other words,
285          * right-justify it) */
286         char *start = max(cmdwin.buf, cmdwin.ptr - cmdwin.slen);
287 	screenErase(&cmdwin.srect);
288 	screenPrint(&cmdwin.srect, 0, start);
289 	screenUpdate(&cmdwin.srect);
290 	cmdwin_repaint_cursor();
291 }
292 
cmdwin_flush(void)293 void cmdwin_flush(void)
294 {
295         if (!strlen(cmdwin.buf))
296                 return;
297 
298         log_msg("%s\n", cmdwin.buf);
299         cmdwin_clear();
300 }
301