home · contact · privacy
7cdb0b5134668af19474c541fa0e39d94cb0a5e6
[plomrogue] / src / client / draw_wins.c
1 /* src/client/draw_wins.c */
2
3 #include "draw_wins.h"
4 #include <ncurses.h> /* attri_t, chtype */
5 #include <stddef.h> /* NULL */
6 #include <stdint.h> /* uint8_t, uint16_t, uint32_t, int16_t */
7 #include <stdio.h> /* for sprintf() */
8 #include <stdlib.h> /* free() */
9 #include <string.h> /* strlen(), strtok() */
10 #include "../common/try_malloc.h" /* try_malloc() */
11 #include "keybindings.h" /* struct KeyBinding, get_keyname_to_keycode() */
12 #include "windows.h" /* struct Win, get_win_by_id() */
13 #include "world.h" /* global world */
14
15
16
17 /* Apply to the winmap of Win "w" the new sizes "new_size_y" and "new_size_x"
18  * to the degree that they extend it. Re-shape the window content accordingly.
19  */
20 static void try_resize_winmap(struct Win * w, int new_size_y, int new_size_x);
21
22 /* In Win "w", write "ch" to coordinate "y"/"x". */
23 static void set_ch_on_yx(struct Win * w, int y, int x, chtype ch);
24
25 /* Add "text" into window "win". Break text at right window edge. Also break at
26  * newlines. If "text" ends in a newline, ignore it.
27  */
28 static void add_text_with_linebreaks(struct Win * win, char * text);
29
30 /* Add "line" into window "w". Apply ncurses attribute "attri" to all
31  * characters drawn. If "attri" is non-zero, fill the entire line until the
32  * right window edge with empty characters, so "attri" applies on those too.
33  */
34 static void add_line(struct Win * w, char * line, attr_t attri);
35
36 /* Write "text" with add_text_with_linebreaks() as not starting from the top but
37  * from bottom of "win". Draw only what fits in window (avoid scroll hints).
38  */
39 static void draw_text_from_bottom(struct Win * win, char * text);
40
41 /* Return keybinding list line via "kb_pp", iterate pointer pointed to by it. */
42 static char * get_kb_line_and_iterate(struct KeyBinding ** kb_pp);
43
44 /* Draw from line "start" on config view for keybindings defined at "kb". */
45 static void draw_keybinding_config(struct Win * w, struct KeyBindingDB * kb,
46                                    uint8_t start);
47
48 /* Draw into window "w" from line "start" on a "title" followed by an empty
49  * line followed by a list of all keybindings starting at kb_p.
50  */
51 static uint16_t draw_titled_keybinding_list(char * title, struct Win * w,
52                                             uint16_t start,
53                                             struct KeyBinding * kb_p);
54
55
56
57 static void try_resize_winmap(struct Win * w, int new_size_y, int new_size_x)
58 {
59     char * f_name = "try_resize_winmap()";
60     if (w->winmap_size.y >= new_size_y && w->winmap_size.x >= new_size_x)
61     {
62         return;
63     }
64     if      (w->winmap_size.y > new_size_y)
65     {
66         new_size_y = w->winmap_size.y;
67     }
68     else if (w->winmap_size.x > new_size_x)
69     {
70         new_size_x = w->winmap_size.x;
71     }
72     chtype * old_winmap = w->winmap;
73     uint32_t new_size = sizeof(chtype) * new_size_y * new_size_x;
74     w->winmap = try_malloc(new_size, f_name);
75     uint16_t y, x;
76     for (y = 0; y < new_size_y; y++)
77     {
78         for (x = 0; y < w->winmap_size.y && x < w->winmap_size.x; x++)
79         {
80             chtype ch = old_winmap[(y * w->winmap_size.x) + x];
81             w->winmap[(y * new_size_x) + x] = ch;
82         }
83         for (; x < new_size_x; x++)
84         {
85             w->winmap[(y * new_size_x) + x] = ' ';
86         }
87     }
88     free(old_winmap);
89     w->winmap_size.y = new_size_y;
90     w->winmap_size.x = new_size_x;
91 }
92
93
94
95 static void set_ch_on_yx(struct Win * w, int y, int x, chtype ch)
96 {
97     w->winmap[(y * w->winmap_size.x) + x] = ch;
98 }
99
100
101
102 static void add_text_with_linebreaks(struct Win * win, char * text)
103 {
104     uint16_t x, y;
105     int16_t z = -1;
106     for (y = win->winmap_size.y; ; y++)
107     {
108         try_resize_winmap(win, y + 1, win->frame_size.x);
109         for (x = 0; x < win->frame_size.x; x++)
110         {
111             z++;
112             if      ('\n' == text[z])
113             {
114                 break;
115             }
116             else if ('\0' == text[z])
117             {
118                 return;
119             }
120             else
121             {
122                 set_ch_on_yx(win, y, x, text[z]);
123             }
124             if      ('\n' == text[z+1])
125             {
126                 z++;
127                 break;
128             }
129             else if ('\0' == text[z+1])
130             {
131                 return;
132             }
133         }
134     }
135 }
136
137
138
139 static void add_line(struct Win * w, char * line, attr_t attri)
140 {
141     uint16_t y = w->winmap_size.y;
142     uint16_t len_line = strlen(line);
143     if (attri
144         && w->winmap_size.x < w->frame_size.x && w->frame_size.x > len_line)
145     {
146         try_resize_winmap(w, y + 1, w->frame_size.x);
147     }
148     else
149     {
150         try_resize_winmap(w, y + 1, strlen(line));
151     }
152     uint16_t x = 0;
153     for (; x < len_line; x++)
154     {
155         set_ch_on_yx(w, y, x, line[x] | attri);
156     }
157     if (attri)
158     {
159         for (; x < w->frame_size.x; x++)
160         {
161             set_ch_on_yx(w, y, x, ' ' | attri);
162         }
163     }
164 }
165
166
167
168 static void draw_text_from_bottom(struct Win * win, char * text)
169 {
170     /* Determine number of lines text would have in a window of win's width,
171      * but infinite height. Treat \n and \0 as control chars for incrementing
172      * y and stopping the loop. Make sure +they* don't count as cell space.
173      */
174     char toggle = 0;
175     uint16_t x, y;
176     int16_t z = -1;
177     for (y = 0; 0 == toggle; y++)
178     {
179         for (x = 0; x < win->frame_size.x; x++)
180         {
181             z++;
182             if ('\n' == text[z])
183             {
184                 break;
185             }
186             if ('\n' == text[z+1])
187             {
188                 z++;
189                 break;
190             }
191             else if (0 == text[z+1])
192             {
193                 toggle = 1;
194                 break;
195             }
196         }
197     }
198     z = -1;
199
200     /* Depending on what's bigger, determine start point in window or text. */
201     uint16_t start_y = 0;
202     if (y < win->frame_size.y)
203     {
204         start_y = win->frame_size.y - y;
205     }
206     else if (y > win->frame_size.y)
207     {
208         uint16_t offset = y - win->frame_size.y;
209         for (y = 0; y < offset; y++)
210         {
211             for (x = 0; x < win->frame_size.x; x++)
212             {
213                 z++;
214                 if ('\n' == text[z])
215                 {
216                     break;
217                 }
218                 if ('\n' == text[z+1])
219                 {
220                     z++;
221                     break;
222                 }
223             }
224         }
225         text = text + (sizeof(char) * (z + 1));
226     }
227
228     try_resize_winmap(win, start_y, 1);
229     add_text_with_linebreaks(win, text);
230 }
231
232
233
234 static char * get_kb_line_and_iterate(struct KeyBinding ** kb_pp)
235 {
236     char * f_name = "get_kb_line_and_iterate()";
237     struct KeyBinding * kb_p = * kb_pp;
238     char * keyname = get_keyname_to_keycode(kb_p->key);
239     struct Command * command = get_command_to_keycode(kb_p, kb_p->key);
240     uint16_t size = 9 + 1 + strlen(command->dsc_long) + 1;
241     char * line = try_malloc(size, f_name);
242     sprintf(line, "%-9s %s", keyname, command->dsc_long);
243     free(keyname);
244     * kb_pp = kb_p->next;
245     return line;
246 }
247
248
249
250 static void draw_keybinding_config(struct Win * w, struct KeyBindingDB * kb,
251                                    uint8_t start)
252 {
253     if (0 == kb->kbs)
254     {
255         add_line(w, "(none)", 0);
256         return;
257     }
258     struct KeyBinding * kb_p = kb->kbs;
259     uint16_t y;
260     for (y = start; 0 != kb_p; y++)
261     {
262         attr_t attri = 0;
263         if (y - start == kb->select)
264         {
265             attri = A_REVERSE;
266             if (1 == kb->edit)
267             {
268                 attri = attri | A_BLINK;
269             }
270         }
271         char * kb_line = get_kb_line_and_iterate(&kb_p);
272         add_line(w, kb_line, attri);
273         free(kb_line);
274     }
275 }
276
277
278
279 static uint16_t draw_titled_keybinding_list(char * title, struct Win * w,
280                                             uint16_t start,
281                                             struct KeyBinding * kb_p)
282 {
283     uint16_t y;
284     uint8_t state = 0;
285     for (y = start; (0 == state || 0 != kb_p); y++)
286     {
287         if (0 == state)
288         {
289             add_line(w, title, 0);
290             y++;
291             add_line(w, " ", 0);
292             state = 1 + (0 == kb_p);
293             continue;
294         }
295         char * kb_line = get_kb_line_and_iterate(&kb_p);
296         add_line(w, kb_line, 0);
297         free(kb_line);
298     }
299     if (2 == state)
300     {
301         char * none = "(none)";
302         add_line(w, none, 0);
303         y++;
304     }
305     return y;
306 }
307
308
309
310 extern void draw_win_log(struct Win * win)
311 {
312     char * log = "";
313     if (world.log)
314     {
315         log = world.log;
316     }
317     draw_text_from_bottom(win, log);
318 }
319
320
321
322 extern void draw_win_map(struct Win * win)
323 {
324     try_resize_winmap(win, world.map.size.y, world.map.size.x);
325     uint16_t z = 0;
326     uint16_t x, y;
327     for (y = 0; y < world.map.size.y; y++)
328     {
329         for (x = 0; x < world.map.size.x; x++)
330         {
331             set_ch_on_yx(win, y, x, world.map.cells[z]);
332             z++;
333         }
334     }
335 }
336
337
338
339 extern void draw_win_info(struct Win * win)
340 {
341     char * dsc_turn      = "Turn: ";
342     char * dsc_hitpoints = "\nHitpoints: ";
343     char * dsc_score     = "\nScore: ";
344     uint16_t maxl = strlen(dsc_turn) + strlen(dsc_hitpoints) + strlen(dsc_score)
345                     + 5 + 3 + 5; /* Max strlens of strings of numbers to use. */
346     char text[maxl + 1];
347     sprintf(text, "%s%d%s%d%s%d",
348             dsc_turn, world.turn,
349             dsc_hitpoints, world.player_lifepoints,
350             dsc_score, world.player_score);
351     add_text_with_linebreaks(win, text);
352 }
353
354
355
356 extern void draw_win_inventory(struct Win * win)
357 {
358     win->center.y = world.player_inventory_select;
359     char inventory_copy[strlen(world.player_inventory) + 1];
360     sprintf(inventory_copy, "%s", world.player_inventory);
361     char * strtok_target = inventory_copy;
362     uint8_t i = 0;
363     while (1)
364     {
365         char * object = strtok(strtok_target, "\n");
366         strtok_target = NULL;
367         if (NULL == object)
368         {
369             return;
370         }
371         attr_t attri = 0;
372         if (i == world.player_inventory_select)
373         {
374             attri = A_REVERSE;
375         }
376         add_line(win, object, attri);
377         i++;
378     }
379 }
380
381
382
383 extern void draw_win_available_keybindings(struct Win * win)
384 {
385     char * title = "Active window's keybindings:";
386     struct KeyBinding * kb_p;
387     struct Win * w = get_win_by_id(world.winDB.active);
388     if     (0 == w->view)
389     {
390         kb_p = w->kb.kbs;
391     }
392     else if (1 == w->view)
393     {
394         kb_p = world.kb_wingeom.kbs;
395     }
396     else if (2 == w->view)
397     {
398         kb_p = world.kb_winkeys.kbs;
399     }
400     uint16_t offset = draw_titled_keybinding_list(title, win, 0, kb_p);
401     add_line(win, " ", 0);
402     struct KeyBinding * kbs_glo = world.kb_global.kbs;
403     draw_titled_keybinding_list("Global keybindings", win, offset + 1, kbs_glo);
404 }
405
406
407
408 extern void draw_win_keybindings_global(struct Win * win)
409 {
410     win->center.y = world.kb_global.select;
411     draw_keybinding_config(win, &world.kb_global, 0);
412 }
413
414
415
416 extern void draw_win_keybindings_winconf_geometry(struct Win * win)
417 {
418     win->center.y = world.kb_wingeom.select;
419     draw_keybinding_config(win, &world.kb_wingeom, 0);
420 }
421
422
423
424 extern void draw_win_keybindings_winconf_keybindings(struct Win * win)
425 {
426     win->center.y = world.kb_winkeys.select;
427     draw_keybinding_config(win, &world.kb_winkeys, 0);
428 }
429
430
431
432 extern void draw_winconf_keybindings(struct Win * win)
433 {
434     char * title = "Window's keybindings:";
435     add_line(win, title, 0);
436     add_line(win, " ", 0);
437     draw_keybinding_config(win, &win->kb, 2);
438     win->center.y = win->kb.select + 2;
439 }
440
441
442
443 extern void draw_winconf_geometry(struct Win * win)
444 {
445     char * title = "Window's geometry:\n";
446     char * h_d   = "\nHeight to save: ";
447     char * h_pos = " (width in cells)";
448     char * h_neg = " (negative diff: cells to screen width)";
449     char * w_d   = "\n\nWidth to save: ";
450     char * w_pos = " (height in cells)";
451     char * w_neg = " (negative diff: cells to screen height)";
452     char * h_t = h_pos;
453     char * w_t = w_pos;
454     if (1 == win->target_height_type)
455     {
456         h_t = h_neg;
457     }
458     if (1 == win->target_width_type)
459     {
460         w_t = w_neg;
461     }
462     uint16_t maxl = strlen(title)
463                     + strlen(h_t) + strlen(h_d) + 6      /* 6 = n of chars to */
464                     + strlen(w_t) + strlen(w_d) + 6 + 1; /* write max int16_t */
465     char text[maxl + 1];
466     sprintf(text, "%s%s%d%s%s%d%s", title, h_d, win->target_height, h_t,
467                                            w_d, win->target_width,  w_t);
468     add_text_with_linebreaks(win, text);
469 }