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